Skip to content

Commit

Permalink
Add sorting to all tables (#8267)
Browse files Browse the repository at this point in the history
  • Loading branch information
egbertbouman authored Nov 14, 2024
2 parents 47bd1e0 + f9f5919 commit 0f5b172
Show file tree
Hide file tree
Showing 21 changed files with 171 additions and 149 deletions.
57 changes: 53 additions & 4 deletions src/tribler/ui/src/components/ui/simple-table.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,55 @@
import { SetStateAction, useEffect, useRef, useState } from 'react';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { getCoreRowModel, useReactTable, flexRender, getFilteredRowModel, getPaginationRowModel, getExpandedRowModel, getSortedRowModel } from '@tanstack/react-table';
import type { ColumnDef, Row, PaginationState, RowSelectionState, ColumnFiltersState, ExpandedState } from '@tanstack/react-table';
import type { ColumnDef, Row, PaginationState, RowSelectionState, ColumnFiltersState, ExpandedState, ColumnDefTemplate, HeaderContext, SortingState } from '@tanstack/react-table';
import { cn } from '@/lib/utils';
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel } from './select';
import { Button } from './button';
import { ChevronLeftIcon, ChevronRightIcon, DoubleArrowLeftIcon, DoubleArrowRightIcon } from '@radix-ui/react-icons';
import { ArrowDownIcon, ArrowUpIcon, ChevronLeftIcon, ChevronRightIcon, DoubleArrowLeftIcon, DoubleArrowRightIcon } from '@radix-ui/react-icons';
import * as SelectPrimitive from "@radix-ui/react-select"
import type { Table as ReactTable } from '@tanstack/react-table';
import { useTranslation } from 'react-i18next';
import { useResizeObserver } from '@/hooks/useResizeObserver';


export function getHeader<T>(name: string, translate: boolean = true, addSorting: boolean = true): ColumnDefTemplate<HeaderContext<T, unknown>> | undefined {
if (!addSorting) {
return () => {
const { t } = useTranslation();
return <span className='select-none'>{translate ? t(name) : name}</span>;
}
}

return ({ column }) => {
const { t } = useTranslation();
return (
<div className='select-none flex'>
<span
className="cursor-pointer hover:text-black dark:hover:text-white flex flex-row items-center"
onClick={() => column.toggleSorting()}>
{translate ? t(name) : name}
{column.getIsSorted() === "desc" ? (
<ArrowDownIcon className="ml-2" />
) : column.getIsSorted() === "asc" ? (
<ArrowUpIcon className="ml-2" />
) : (
<></>
)}
</span>
</div>
)
}
}

function getStoredSortingState(key?: string) {
if (key) {
let sortingString = localStorage.getItem(key);
if (sortingString) {
return JSON.parse(sortingString);
}
}
}

interface ReactTableProps<T extends object> {
data: T[];
columns: ColumnDef<T>[];
Expand All @@ -29,6 +67,7 @@ interface ReactTableProps<T extends object> {
filters?: { id: string, value: string }[];
maxHeight?: string | number;
expandable?: boolean;
storeSortingState?: string;
}

function SimpleTable<T extends object>({
Expand All @@ -46,7 +85,8 @@ function SimpleTable<T extends object>({
allowMultiSelect,
filters,
maxHeight,
expandable
expandable,
storeSortingState
}: ReactTableProps<T>) {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: pageIndex ?? 0,
Expand All @@ -55,6 +95,7 @@ function SimpleTable<T extends object>({
const [rowSelection, setRowSelection] = useState<RowSelectionState>(initialRowSelection || {});
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(filters || [])
const [expanded, setExpanded] = useState<ExpandedState>({});
const [sorting, setSorting] = useState<SortingState>(getStoredSortingState(storeSortingState) || []);

const table = useReactTable({
data,
Expand All @@ -69,7 +110,8 @@ function SimpleTable<T extends object>({
pagination,
rowSelection,
columnFilters,
expanded
expanded,
sorting
},
getFilteredRowModel: getFilteredRowModel(),
onColumnFiltersChange: setColumnFilters,
Expand All @@ -78,6 +120,7 @@ function SimpleTable<T extends object>({
if (allowSelect || allowSelectCheckbox || allowMultiSelect) setRowSelection(arg);
},
onExpandedChange: setExpanded,
onSortingChange: setSorting,
getSubRows: (row: any) => row?.subRows,
});

Expand All @@ -104,6 +147,12 @@ function SimpleTable<T extends object>({
}
}, [filters])

useEffect(() => {
if (storeSortingState) {
localStorage.setItem(storeSortingState, JSON.stringify(sorting));
}
}, [sorting]);

// For some reason the ScrollArea scrollbar is only shown when it's set to a specific height.
// So, we wrap it in a parent div, monitor its size, and set the height of the table accordingly.
const parentRef = useRef<HTMLTableElement>(null);
Expand Down
8 changes: 4 additions & 4 deletions src/tribler/ui/src/dialogs/SaveAs.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import SimpleTable from "@/components/ui/simple-table";
import SimpleTable, { getHeader } from "@/components/ui/simple-table";
import { useEffect, useMemo, useState } from "react";
import toast from 'react-hot-toast';
import { triblerService } from "@/services/tribler.service";
import { isErrorDict } from "@/services/reporting";
import { filesToTree, fixTreeProps, formatBytes, getFilesFromMetainfo, getRowSelection, getSelectedFilesFromTree, translateHeader } from "@/lib/utils";
import { filesToTree, fixTreeProps, formatBytes, getFilesFromMetainfo, getRowSelection, getSelectedFilesFromTree } from "@/lib/utils";
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { DialogProps } from "@radix-ui/react-dialog";
Expand Down Expand Up @@ -32,7 +32,7 @@ function startDownloadCallback(response: any, t: TFunction) {
const getFileColumns = ({ onSelectedFiles }: { onSelectedFiles: (row: Row<FileTreeItem>) => void }): ColumnDef<FileTreeItem>[] => [
{
accessorKey: "name",
header: translateHeader('Name'),
header: getHeader('Name'),
cell: ({ row }) => {
return (
<div
Expand All @@ -55,7 +55,7 @@ const getFileColumns = ({ onSelectedFiles }: { onSelectedFiles: (row: Row<FileTr
},
{
accessorKey: "size",
header: translateHeader('Size'),
header: getHeader('Size'),
cell: ({ row }) => {
return (
<div className='flex items-center'>
Expand Down
8 changes: 0 additions & 8 deletions src/tribler/ui/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import es from 'javascript-time-ago/locale/es'
import pt from 'javascript-time-ago/locale/pt'
import ru from 'javascript-time-ago/locale/ru'
import zh from 'javascript-time-ago/locale/zh'
import Cookies from "js-cookie";
import { useTranslation } from "react-i18next";
import { triblerService } from "@/services/tribler.service";
import { FileLink, FileTreeItem } from "@/models/file.model";
Expand Down Expand Up @@ -141,13 +140,6 @@ export function getRowSelection(input: any[], selected_func: (item: any) => bool
return selection;
}

export function translateHeader(name: string) {
return () => {
const { t } = useTranslation();
return t(name);
}
}

export function filterDuplicates(data: any[], key: string) {
const seen = new Set();
return data.filter(item => {
Expand Down
12 changes: 6 additions & 6 deletions src/tribler/ui/src/pages/Debug/Asyncio/Tasks.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import SimpleTable from "@/components/ui/simple-table";
import SimpleTable, { getHeader } from "@/components/ui/simple-table";
import { ColumnDef } from "@tanstack/react-table";
import { useState } from "react";
import { ipv8Service } from "@/services/ipv8.service";
Expand All @@ -11,26 +11,26 @@ import { formatTimeDiff } from "@/lib/utils";
const taskColumns: ColumnDef<Task>[] = [
{
accessorKey: "taskmanager",
header: "Taskmanager",
header: getHeader("Taskmanager", false),
},
{
accessorKey: "name",
header: "Name",
header: getHeader("Name", false),
cell: ({ row }) => {
return <span className="line-clamp-1">{row.original.name}</span>
},
},
{
accessorKey: "running",
header: "Running?",
header: getHeader("Running?", false),
},
{
accessorKey: "interval",
header: "Interval",
header: getHeader("Interval", false),
},
{
accessorKey: "start_time",
header: "Started",
header: getHeader("Started", false),
cell: ({ row }) => {
return row.original.start_time && <span>{formatTimeDiff(row.original.start_time)}</span>
},
Expand Down
8 changes: 4 additions & 4 deletions src/tribler/ui/src/pages/Debug/DHT/Buckets.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import SimpleTable from "@/components/ui/simple-table";
import SimpleTable, { getHeader } from "@/components/ui/simple-table";
import { useState } from "react";
import { ipv8Service } from "@/services/ipv8.service";
import { isErrorDict } from "@/services/reporting";
Expand All @@ -11,18 +11,18 @@ import { useInterval } from '@/hooks/useInterval';
const bucketColumns: ColumnDef<Bucket>[] = [
{
accessorKey: "prefix",
header: "Prefix",
header: getHeader("Prefix", false),
},
{
accessorKey: "last_changed",
header: "Last changed",
header: getHeader("Last changed", false),
cell: ({ row }) => {
return <span>{formatTimeDiff(row.original.last_changed)}</span>
},
},
{
accessorKey: "peer",
header: "# Peers",
header: getHeader("# Peers", false),
cell: ({ row }) => {
return <span>{row.original.peers.length}</span>
},
Expand Down
6 changes: 3 additions & 3 deletions src/tribler/ui/src/pages/Debug/DHT/Statistics.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import SimpleTable from "@/components/ui/simple-table";
import SimpleTable, { getHeader } from "@/components/ui/simple-table";
import { useState } from "react";
import { ipv8Service } from "@/services/ipv8.service";
import { isErrorDict } from "@/services/reporting";
Expand All @@ -10,11 +10,11 @@ import { useInterval } from '@/hooks/useInterval';
const statisticColumns: ColumnDef<KeyValue>[] = [
{
accessorKey: "key",
header: "Key",
header: getHeader("Key", false),
},
{
accessorKey: "value",
header: "Value",
header: getHeader("Value", false),
cell: ({ row }) => {
return <span className="whitespace-pre">{row.original.value}</span>
},
Expand Down
6 changes: 3 additions & 3 deletions src/tribler/ui/src/pages/Debug/General/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import SimpleTable from "@/components/ui/simple-table";
import SimpleTable, { getHeader } from "@/components/ui/simple-table";
import { formatBytes } from "@/lib/utils";
import { KeyValue } from "@/models/keyvalue.model";
import { triblerService } from "@/services/tribler.service";
Expand All @@ -11,11 +11,11 @@ import { useInterval } from '@/hooks/useInterval';
const generalColumns: ColumnDef<KeyValue>[] = [
{
accessorKey: "key",
header: "Key",
header: getHeader("Key", false),
},
{
accessorKey: "value",
header: "Value",
header: getHeader("Value", false),
},
]

Expand Down
12 changes: 6 additions & 6 deletions src/tribler/ui/src/pages/Debug/IPv8/Details.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import SimpleTable from "@/components/ui/simple-table";
import SimpleTable, { getHeader } from "@/components/ui/simple-table";
import { useState } from "react";
import { ipv8Service } from "@/services/ipv8.service";
import { isErrorDict } from "@/services/reporting";
Expand All @@ -10,35 +10,35 @@ import { useInterval } from '@/hooks/useInterval';
const statisticColumns: ColumnDef<OverlayMsgStats>[] = [
{
accessorKey: "name",
header: "Name",
header: getHeader("Name", false),
},
{
accessorKey: "bytes_up",
header: "Upload (MB)",
header: getHeader("Upload (MB)", false),
cell: ({ row }) => {
if (row.original.identifier < 0) { return }
return <span>{(row.original.bytes_up / 1024 ** 2).toFixed(3)}</span>
},
},
{
accessorKey: "bytes_down",
header: "Download (MB)",
header: getHeader("Download (MB)", false),
cell: ({ row }) => {
if (row.original.identifier < 0) { return }
return <span>{(row.original.bytes_down / 1024 ** 2).toFixed(3)}</span>
},
},
{
accessorKey: "num_up",
header: "# Msgs sent",
header: getHeader("# Msgs sent", false),
cell: ({ row }) => {
if (row.original.identifier < 0) { return }
return <span>{row.original.num_up}</span>
},
},
{
accessorKey: "num_down",
header: "# Msgs received",
header: getHeader("# Msgs received", false),
cell: ({ row }) => {
if (row.original.identifier < 0) { return }
return <span>{row.original.num_down}</span>
Expand Down
Loading

0 comments on commit 0f5b172

Please sign in to comment.