2024-05-21 01:08:45 +02:00
|
|
|
// Copyright 2024, Command Line Inc.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2024-05-28 21:12:28 +02:00
|
|
|
import { Table, createColumnHelper, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
2024-06-04 03:22:26 +02:00
|
|
|
import clsx from "clsx";
|
2024-05-29 09:00:36 +02:00
|
|
|
import * as jotai from "jotai";
|
2024-05-28 21:12:28 +02:00
|
|
|
import React from "react";
|
2024-05-21 01:08:45 +02:00
|
|
|
|
2024-05-29 09:00:36 +02:00
|
|
|
import "./directorypreview.less";
|
2024-05-21 01:08:45 +02:00
|
|
|
|
|
|
|
interface DirectoryTableProps {
|
|
|
|
data: FileInfo[];
|
2024-06-03 22:24:20 +02:00
|
|
|
cwd: string;
|
2024-05-29 09:00:36 +02:00
|
|
|
setFileName: (_: string) => void;
|
2024-05-21 01:08:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const columnHelper = createColumnHelper<FileInfo>();
|
|
|
|
|
|
|
|
const defaultColumns = [
|
|
|
|
columnHelper.accessor("path", {
|
|
|
|
cell: (info) => info.getValue(),
|
|
|
|
header: () => <span>Name</span>,
|
|
|
|
}),
|
|
|
|
columnHelper.accessor("size", {
|
|
|
|
cell: (info) => info.getValue(),
|
|
|
|
header: () => <span>Size</span>,
|
|
|
|
}),
|
|
|
|
columnHelper.accessor("mimetype", {
|
|
|
|
cell: (info) => info.getValue(),
|
2024-06-03 22:24:20 +02:00
|
|
|
header: () => <span>Type</span>,
|
2024-05-21 01:08:45 +02:00
|
|
|
}),
|
|
|
|
];
|
|
|
|
|
2024-06-03 22:24:20 +02:00
|
|
|
function DirectoryTable({ data, cwd, setFileName }: DirectoryTableProps) {
|
2024-05-21 01:08:45 +02:00
|
|
|
const [columns] = React.useState<typeof defaultColumns>(() => [...defaultColumns]);
|
|
|
|
const table = useReactTable({
|
|
|
|
data,
|
|
|
|
columns,
|
|
|
|
columnResizeMode: "onChange",
|
|
|
|
getCoreRowModel: getCoreRowModel(),
|
|
|
|
});
|
|
|
|
|
2024-05-21 21:14:04 +02:00
|
|
|
const columnSizeVars = React.useMemo(() => {
|
|
|
|
const headers = table.getFlatHeaders();
|
|
|
|
const colSizes: { [key: string]: number } = {};
|
|
|
|
for (let i = 0; i < headers.length; i++) {
|
|
|
|
const header = headers[i]!;
|
|
|
|
colSizes[`--header-${header.id}-size`] = header.getSize();
|
|
|
|
colSizes[`--col-${header.column.id}-size`] = header.column.getSize();
|
|
|
|
}
|
|
|
|
return colSizes;
|
|
|
|
}, [table.getState().columnSizingInfo]);
|
|
|
|
|
2024-05-21 01:08:45 +02:00
|
|
|
return (
|
2024-05-21 21:14:04 +02:00
|
|
|
<div className="dir-table" style={{ ...columnSizeVars }}>
|
|
|
|
<div className="dir-table-head">
|
2024-05-21 01:08:45 +02:00
|
|
|
{table.getHeaderGroups().map((headerGroup) => (
|
2024-05-21 21:14:04 +02:00
|
|
|
<div className="dir-table-head-row" key={headerGroup.id}>
|
2024-05-21 01:08:45 +02:00
|
|
|
{headerGroup.headers.map((header) => (
|
2024-05-21 21:14:04 +02:00
|
|
|
<div
|
|
|
|
className="dir-table-head-cell"
|
|
|
|
key={header.id}
|
|
|
|
style={{ width: `calc(var(--header-${header.id}-size) * 1px)` }}
|
|
|
|
>
|
2024-05-21 01:08:45 +02:00
|
|
|
{header.isPlaceholder
|
|
|
|
? null
|
|
|
|
: flexRender(header.column.columnDef.header, header.getContext())}
|
2024-05-21 21:14:04 +02:00
|
|
|
<div
|
|
|
|
className="dir-table-head-resize"
|
|
|
|
onMouseDown={header.getResizeHandler()}
|
|
|
|
onTouchStart={header.getResizeHandler()}
|
|
|
|
/>
|
|
|
|
</div>
|
2024-05-21 01:08:45 +02:00
|
|
|
))}
|
2024-05-21 21:14:04 +02:00
|
|
|
</div>
|
2024-05-21 01:08:45 +02:00
|
|
|
))}
|
2024-05-21 21:14:04 +02:00
|
|
|
</div>
|
|
|
|
{table.getState().columnSizingInfo.isResizingColumn ? (
|
2024-06-03 22:24:20 +02:00
|
|
|
<MemoizedTableBody table={table} cwd={cwd} setFileName={setFileName} />
|
2024-05-21 21:14:04 +02:00
|
|
|
) : (
|
2024-06-03 22:24:20 +02:00
|
|
|
<TableBody table={table} cwd={cwd} setFileName={setFileName} />
|
2024-05-21 21:14:04 +02:00
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-05-29 09:00:36 +02:00
|
|
|
interface TableBodyProps {
|
|
|
|
table: Table<FileInfo>;
|
2024-06-03 22:24:20 +02:00
|
|
|
cwd: string;
|
2024-05-29 09:00:36 +02:00
|
|
|
setFileName: (_: string) => void;
|
|
|
|
}
|
|
|
|
|
2024-06-03 22:24:20 +02:00
|
|
|
function TableBody({ table, cwd, setFileName }: TableBodyProps) {
|
2024-05-21 21:14:04 +02:00
|
|
|
return (
|
|
|
|
<div className="dir-table-body">
|
|
|
|
{table.getRowModel().rows.map((row) => (
|
2024-05-29 09:00:36 +02:00
|
|
|
<div
|
|
|
|
className="dir-table-body-row"
|
|
|
|
key={row.id}
|
|
|
|
tabIndex={0}
|
|
|
|
onDoubleClick={() => {
|
|
|
|
const newFileName = row.getValue("path") as string;
|
2024-06-03 22:24:20 +02:00
|
|
|
const fullPath = cwd.concat("/", newFileName);
|
|
|
|
setFileName(fullPath);
|
2024-05-29 09:00:36 +02:00
|
|
|
}}
|
|
|
|
>
|
2024-06-03 22:24:20 +02:00
|
|
|
{row.getVisibleCells().map((cell) => {
|
|
|
|
return (
|
|
|
|
<div
|
2024-06-04 03:22:26 +02:00
|
|
|
className={clsx("dir-table-body-cell", "col-" + cell.column.id)}
|
2024-06-03 22:24:20 +02:00
|
|
|
key={cell.id}
|
|
|
|
style={{ width: `calc(var(--col-${cell.column.id}-size) * 1px)` }}
|
|
|
|
>
|
|
|
|
{cell.renderValue<string>()}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
})}
|
2024-05-21 21:14:04 +02:00
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
2024-05-21 01:08:45 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-05-21 21:14:04 +02:00
|
|
|
const MemoizedTableBody = React.memo(
|
|
|
|
TableBody,
|
|
|
|
(prev, next) => prev.table.options.data == next.table.options.data
|
|
|
|
) as typeof TableBody;
|
|
|
|
|
2024-05-29 09:00:36 +02:00
|
|
|
interface DirectoryPreviewProps {
|
|
|
|
contentAtom: jotai.Atom<Promise<string>>;
|
|
|
|
fileNameAtom: jotai.WritableAtom<string, [string], void>;
|
|
|
|
}
|
|
|
|
|
|
|
|
function DirectoryPreview({ contentAtom, fileNameAtom }: DirectoryPreviewProps) {
|
|
|
|
const contentText = jotai.useAtomValue(contentAtom);
|
|
|
|
let content: FileInfo[] = JSON.parse(contentText);
|
|
|
|
let [fileName, setFileName] = jotai.useAtom(fileNameAtom);
|
2024-06-03 22:24:20 +02:00
|
|
|
return <DirectoryTable data={content} cwd={fileName} setFileName={setFileName} />;
|
2024-05-29 09:00:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export { DirectoryPreview };
|