file browser small UI improvements
This commit is contained in:
parent
68c5f602f9
commit
b8226d44d6
1 changed files with 36 additions and 29 deletions
|
|
@ -33,7 +33,6 @@ function formatSize(bytes?: number) {
|
|||
function formatDate(ms?: number) {
|
||||
if (!ms) return "—";
|
||||
const d = new Date(ms);
|
||||
// date only, short & compact
|
||||
return d.toLocaleDateString(undefined, { year: "numeric", month: "numeric", day: "numeric" });
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +47,7 @@ export default function FileBrowserPanel() {
|
|||
const p = encodeURIComponent(path || "/");
|
||||
return `${BASE}/api/files/list?path=${p}`;
|
||||
}, [path]);
|
||||
|
||||
const urlDownload = useCallback((p: string) => {
|
||||
const qp = encodeURIComponent(p || "/");
|
||||
return `${BASE}/api/files/download?path=${qp}`;
|
||||
|
|
@ -62,7 +62,7 @@ export default function FileBrowserPanel() {
|
|||
setError(null);
|
||||
setPreviewHref(null);
|
||||
try {
|
||||
const res = await fetch(urlList, { method: "GET", credentials: "include", headers: { Accept: "application/json" }, cache: "no-store" });
|
||||
const res = await fetch(urlList, { headers: { Accept: "application/json" }, cache: "no-store" });
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const json: ListResponse = await res.json();
|
||||
const arr = (json as any).items || (json as any).entries || [];
|
||||
|
|
@ -93,10 +93,24 @@ export default function FileBrowserPanel() {
|
|||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{/* keep typography consistent */}
|
||||
{/* compact, consistent type + strict columns */}
|
||||
<style>{`
|
||||
.fs-cell{ font-size: 13.5px; line-height: 1.3; }
|
||||
.fs-tight{ letter-spacing: .01em; }
|
||||
.fs-cell{ font-size:13.5px; line-height:1.3 }
|
||||
.fs-tight{ letter-spacing:.01em }
|
||||
.fs-table{
|
||||
--col-name-min: 260px;
|
||||
--col-type: 56px;
|
||||
--col-size: 72px;
|
||||
--col-date: 96px;
|
||||
--col-actions: 44px; /* icon-only */
|
||||
display:grid;
|
||||
grid-template-columns:minmax(var(--col-name-min),1fr) var(--col-type) var(--col-size) var(--col-date) var(--col-actions);
|
||||
}
|
||||
.fs-nowrap{ white-space:nowrap }
|
||||
.fs-iconbtn{
|
||||
width:28px; height:28px; display:inline-flex; align-items:center; justify-content:center;
|
||||
border:1px solid var(--border); border-radius:8px;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{/* Toolbar */}
|
||||
|
|
@ -116,25 +130,19 @@ export default function FileBrowserPanel() {
|
|||
|
||||
<div className="text-xs text-muted-foreground select-all">{path || "/"}</div>
|
||||
|
||||
{/* Two-column layout: left auto, right fixed min (no horizontal scroll in left) */}
|
||||
<div className="grid grid-cols-[minmax(640px,1fr)_minmax(420px,42vw)] gap-3">
|
||||
{/* LEFT: table (no horizontal scrolling; columns sized to fit) */}
|
||||
{/* Layout: left flex, right fixed min; no horizontal scroll on left */}
|
||||
<div className="grid grid-cols-[minmax(560px,1fr)_minmax(420px,42vw)] gap-3 items-start">
|
||||
{/* LEFT: Table */}
|
||||
<div className="rounded-md border overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="grid items-center bg-muted px-3 py-2 fs-cell fs-tight"
|
||||
style={{
|
||||
gridTemplateColumns:
|
||||
// Name grows; Type/Size compact; Date compact; Actions fixed
|
||||
"minmax(280px,1fr) 64px 84px 112px 118px",
|
||||
}}>
|
||||
<div>Name</div>
|
||||
<div className="fs-table bg-muted px-2 py-2 fs-cell fs-tight">
|
||||
<div className="px-1">Name</div>
|
||||
<div className="text-center">Type</div>
|
||||
<div className="text-right">Size</div>
|
||||
<div className="text-center">Date</div>
|
||||
<div className="text-right">Actions</div>
|
||||
<div className="text-right pr-1 fs-nowrap">Act</div>
|
||||
</div>
|
||||
|
||||
{/* Rows */}
|
||||
<div className="divide-y">
|
||||
{loading ? (
|
||||
<div className="p-6 text-sm text-muted-foreground flex items-center gap-2">
|
||||
|
|
@ -146,11 +154,10 @@ export default function FileBrowserPanel() {
|
|||
<div className="p-6 text-sm text-muted-foreground">Empty folder.</div>
|
||||
) : (
|
||||
items.map((it) => (
|
||||
<div key={it.name + it.type} className="grid items-center px-3 py-2 hover:bg-muted/50 fs-cell"
|
||||
style={{ gridTemplateColumns: "minmax(280px,1fr) 64px 84px 112px 118px" }}>
|
||||
{/* Name (truncate, tooltipped) */}
|
||||
<div key={it.name + it.type} className="fs-table items-center px-2 py-2 hover:bg-muted/50 fs-cell">
|
||||
{/* Name */}
|
||||
<button
|
||||
className="text-left inline-flex items-center gap-2 hover:underline min-w-0"
|
||||
className="text-left inline-flex items-center gap-2 hover:underline min-w-0 px-1"
|
||||
onClick={() => onOpen(it)}
|
||||
title={it.name}
|
||||
>
|
||||
|
|
@ -164,19 +171,19 @@ export default function FileBrowserPanel() {
|
|||
{/* Size */}
|
||||
<div className="text-right text-muted-foreground">{formatSize(it.size)}</div>
|
||||
|
||||
{/* Date (no time) */}
|
||||
{/* Date only */}
|
||||
<div className="text-center text-muted-foreground">{formatDate(it.mtimeMs)}</div>
|
||||
|
||||
{/* Actions (fixed width, never wraps) */}
|
||||
<div className="text-right">
|
||||
{/* Actions (icon-only, fits in 44px) */}
|
||||
<div className="text-right pr-1">
|
||||
{it.type === "file" && (
|
||||
<button
|
||||
onClick={() => onDownload(it)}
|
||||
className="inline-flex items-center gap-1 rounded-md border px-2 py-1 text-[12px] hover:bg-muted"
|
||||
className="fs-iconbtn hover:bg-muted"
|
||||
title="Download"
|
||||
aria-label={`Download ${it.name}`}
|
||||
>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
Download
|
||||
<Download className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -186,10 +193,10 @@ export default function FileBrowserPanel() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* RIGHT: preview (fixed min width; grows with viewport) */}
|
||||
{/* RIGHT: Preview */}
|
||||
<div className="rounded-md border p-3">
|
||||
<div className="mb-2 text-sm font-medium">Preview</div>
|
||||
<div className="border rounded overflow-hidden" style={{ height: 480 }}>
|
||||
<div className="border rounded overflow-hidden" style={{ height: 520 }}>
|
||||
{previewHref ? (
|
||||
<iframe key={previewHref} src={previewHref} className="w-full h-full" title="Preview" />
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue