1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-24 15:59:41 +02:00

feat(docker): migrate files table to react [EE-4663] (#8916)

This commit is contained in:
Chaim Lev-Ari 2023-07-16 10:59:58 +03:00 committed by GitHub
parent 146681e1c7
commit 09f60c3277
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 529 additions and 229 deletions

View file

@ -0,0 +1,58 @@
import { CellContext } from '@tanstack/react-table';
import { Download, Edit, Trash2 } from 'lucide-react';
import { Authorized } from '@/react/hooks/useUser';
import { Button } from '@@/buttons';
import { FileData, isFilesTableMeta } from '../types';
export function ActionsCell({
row: { original: item },
table,
}: CellContext<FileData, unknown>) {
const { meta } = table.options;
if (!isFilesTableMeta(meta)) {
throw new Error('Invalid table meta');
}
return (
<div className="flex gap-2">
{!item.Dir && (
<Authorized authorizations="DockerAgentBrowseGet">
<Button
color="secondary"
size="xsmall"
onClick={() => meta.onDownload(item.Name)}
icon={Download}
className="!m-0"
>
Download
</Button>
</Authorized>
)}
<Authorized authorizations="DockerAgentBrowseRename">
<Button
color="secondary"
size="xsmall"
icon={Edit}
onClick={() => meta.setIsEdit(item.Name, true)}
className="!m-0"
>
Rename
</Button>
</Authorized>
<Authorized authorizations="DockerAgentBrowseDelete">
<Button
color="dangerlight"
size="xsmall"
icon={Trash2}
onClick={() => meta.onDelete(item.Name)}
className="!m-0"
>
Delete
</Button>
</Authorized>
</div>
);
}

View file

@ -0,0 +1,98 @@
import { CellContext } from '@tanstack/react-table';
import { Check, File as FileIcon, Folder, X } from 'lucide-react';
import { Form, Formik } from 'formik';
import { Icon } from '@@/Icon';
import { Button } from '@@/buttons';
import { Input } from '@@/form-components/Input';
import { FileData, isFilesTableMeta } from '../types';
export function NameCell({
getValue,
row: { original: item },
table,
}: CellContext<FileData, string>) {
const name = getValue();
const { meta } = table.options;
if (!isFilesTableMeta(meta)) {
throw new Error('Invalid table meta');
}
const isEdit = meta.isEdit(name);
if (item.custom) {
return item.custom;
}
if (isEdit) {
return (
<EditForm
originalName={name}
onSave={handleRename}
onClose={() => meta.setIsEdit(name, false)}
/>
);
}
return (
<>
{item.Dir ? (
<Button
color="link"
className="!ml-0 p-0"
onClick={() => meta.onBrowse(name)}
icon={Folder}
>
{name}
</Button>
) : (
<span className="vertical-center">
<Icon icon={FileIcon} />
{name}
</span>
)}
</>
);
function handleRename(name: string) {
if (!isFilesTableMeta(meta)) {
throw new Error('Invalid table meta');
}
meta.onRename(item.Name, name);
meta.setIsEdit(name, false);
}
}
function EditForm({
originalName,
onSave,
onClose,
}: {
originalName: string;
onSave: (name: string) => void;
onClose: () => void;
}) {
return (
<Formik
initialValues={{ name: originalName }}
onSubmit={({ name }) => onSave(name)}
onReset={onClose}
>
{({ values, setFieldValue }) => (
<Form className="flex items-center">
<Input
name="name"
value={values.name}
onChange={(e) => setFieldValue('name', e.target.value)}
className="input-sm w-auto"
/>
<Button color="none" type="reset" icon={X} />
<Button color="none" type="submit" icon={Check} />
</Form>
)}
</Formik>
);
}

View file

@ -0,0 +1,5 @@
import { createColumnHelper } from '@tanstack/react-table';
import { FileData } from '../types';
export const columnHelper = createColumnHelper<FileData>();

View file

@ -0,0 +1,44 @@
import {
CellContext,
ColumnDef,
ColumnDefTemplate,
} from '@tanstack/react-table';
import { humanize, isoDateFromTimestamp } from '@/portainer/filters/filters';
import { FileData } from '../types';
import { columnHelper } from './helper';
import { NameCell } from './NameCell';
import { ActionsCell } from './ActionsCell';
export const columns = [
columnHelper.accessor('Name', {
header: 'Name',
cell: NameCell,
}),
columnHelper.accessor('Size', {
header: 'Size',
cell: hideIfCustom(({ getValue }) => humanize(getValue())),
}),
columnHelper.accessor('ModTime', {
header: 'Last modification',
cell: hideIfCustom(({ getValue }) => isoDateFromTimestamp(getValue())),
}),
columnHelper.display({
header: 'Actions',
cell: hideIfCustom(ActionsCell),
}),
columnHelper.accessor('Dir', {}), // workaround, to enable sorting by Dir (put directory first)
] as ColumnDef<FileData>[];
function hideIfCustom<TValue>(
template: ColumnDefTemplate<CellContext<FileData, TValue>>
): ColumnDefTemplate<CellContext<FileData, TValue>> {
return (props) => {
if (props.row.original.custom) {
return null;
}
return typeof template === 'string' ? template : template(props);
};
}