add column ordering

This commit is contained in:
2024-07-14 16:59:11 +02:00
parent 7ff64879c1
commit 1c976ecda1
5 changed files with 112 additions and 19 deletions

Binary file not shown.

View File

@@ -11,6 +11,9 @@
"preview": "vite preview"
},
"dependencies": {
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@fontsource/inter": "^5.0.19",
"@hookform/resolvers": "^3.9.0",
"@it-incubator/prettier-config": "^0.1.2",

View File

@@ -5,13 +5,42 @@ import {
DropdownMenuContent,
DropdownMenuTrigger,
} from "@/components/ui";
import type { Table } from "@tanstack/react-table";
import {
DndContext,
type DragEndEvent,
KeyboardSensor,
PointerSensor,
closestCenter,
useSensor,
useSensors,
} from "@dnd-kit/core";
import {
SortableContext,
arrayMove,
sortableKeyboardCoordinates,
useSortable,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import type { Column, Table } from "@tanstack/react-table";
import { GripVertical } from "lucide-react";
import type { Dispatch, SetStateAction } from "react";
export function ColumnsDropdown<T>({
table,
columnOrder,
setColumnOrder,
}: {
table: Table<T>;
columnOrder: string[];
setColumnOrder: Dispatch<SetStateAction<string[]>>;
}) {
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
}),
);
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -20,22 +49,69 @@ export function ColumnsDropdown<T>({
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
onSelect={(e) => e.preventDefault()}
checked={column.getIsVisible()}
onCheckedChange={column.toggleVisibility}
>
{column.id}
</DropdownMenuCheckboxItem>
);
})}
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext
items={columnOrder}
strategy={verticalListSortingStrategy}
>
{columnOrder.map((columnId) => {
const column = table.getColumn(columnId);
if (!column) return null;
return <SortableItem key={column.id} column={column} />;
})}
</SortableContext>
</DndContext>
</DropdownMenuContent>
</DropdownMenu>
);
function handleDragEnd(event: DragEndEvent) {
const { active, over } = event;
if (active.id !== over?.id) {
setColumnOrder((items) => {
const oldIndex = items.findIndex((id) => id === active.id);
const newIndex = items.findIndex((id) => id === over.id);
return arrayMove(items, oldIndex, newIndex);
});
}
}
}
const SortableItem = ({ column }: { column: Column<any> }) => {
const {
attributes,
listeners,
setNodeRef,
setActivatorNodeRef,
transform,
transition,
} = useSortable({ id: column.id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<DropdownMenuCheckboxItem
ref={setNodeRef}
style={style}
{...attributes}
key={column.id}
onSelect={(e) => e.preventDefault()}
checked={column.getIsVisible()}
onCheckedChange={column.toggleVisibility}
>
<div className="flex items-center gap-2 justify-between w-full">
{column.id}
<button ref={setActivatorNodeRef} {...listeners} className={"shrink-0"}>
<GripVertical />
</button>
</div>
</DropdownMenuCheckboxItem>
);
};

View File

@@ -20,6 +20,7 @@ const buildColumns = ({
accessorKey: column_name,
title: column_name,
size: 300,
id: column_name,
header: () => {
return (
<div

View File

@@ -24,7 +24,7 @@ import {
useReactTable,
} from "@tanstack/react-table";
import { Rows3 } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useLocalStorage } from "usehooks-ts";
import { z } from "zod";
@@ -42,6 +42,7 @@ export const Route = createFileRoute("/db/$dbName/tables/$tableName/data")({
function Component() {
const { tableName, dbName } = Route.useParams();
const { filters, setFilters } = useFilters(Route.fullPath);
const [whereQuery, setWhereQuery] = useState("");
const [columnSizing, setColumnSizing] = useLocalStorage<ColumnSizingState>(
@@ -64,7 +65,13 @@ function Component() {
const sortingState = useMemo(() => sortByToState(filters), [filters]);
const columns = useColumns({ dbName, tableName });
const [columnOrder, setColumnOrder] = useState<string[]>(() =>
columns.map((c) => c.id ?? ""),
);
useEffect(() => {
if (columnOrder.length !== 0) return;
setColumnOrder(columns.map((c) => c.id ?? ""));
}, [columns, columnOrder.length]);
const { data, refetch } = useTableDataQuery({
whereQuery,
tableName,
@@ -115,12 +122,14 @@ function Component() {
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
manualSorting: true,
onColumnOrderChange: setColumnOrder,
onColumnSizingChange: setColumnSizing,
onColumnVisibilityChange: setColumnVisibility,
onPaginationChange: handlePaginationChange,
onSortingChange: handleSortingChange,
rowCount: data?.count ?? 0,
state: {
columnOrder,
sorting: sortingState,
columnSizing: columnSizing,
columnVisibility,
@@ -136,7 +145,11 @@ function Component() {
<Rows3 /> {tableName}
<WhereClauseForm onSubmit={handleWhereClauseFormSubmit} />
</h1>
<ColumnsDropdown table={table} />
<ColumnsDropdown
table={table}
columnOrder={columnOrder}
setColumnOrder={setColumnOrder}
/>
<p>
Rows: <strong>{data?.count}</strong>
</p>