wip: editable rows

This commit is contained in:
2024-07-16 11:57:58 +02:00
parent d9a01fbd83
commit 2deb472b75
5 changed files with 128 additions and 44 deletions

View File

@@ -0,0 +1,26 @@
import { cn } from "@/lib/utils";
import type { ComponentPropsWithoutRef } from "react";
export function BodyCell({
value,
dataType,
className,
...rest
}: { value: any; dataType: string } & Omit<
ComponentPropsWithoutRef<"div">,
"children"
>) {
return (
<div
className={cn(
"break-all",
["integer", "int", "tinyint", "double"].includes(dataType) &&
"text-right",
className,
)}
{...rest}
>
{value}
</div>
);
}

View File

@@ -1,18 +1,17 @@
import { cn, isImageUrl, isUrl } from "@/lib/utils";
import { BodyCell } from "@/components/db-table-view/body-cell";
import { UrlWithPreview } from "@/components/db-table-view/url-with-preview";
import { cn, isUrl } from "@/lib/utils";
import { type TableColumns, useTableColumnsQuery } from "@/services/db";
import { useSettingsStore } from "@/state";
import type { ColumnDef } from "@tanstack/react-table";
import { useMemo } from "react";
import { useMemo, useState } from "react";
const buildColumns = <T,>({
columns,
formatDates,
showImagesPreview,
}: {
columns?: TableColumns;
formatDates: boolean;
showImagesPreview: boolean;
}): ColumnDef<T>[] => {
if (!columns) return [] as ColumnDef<T>[];
@@ -21,6 +20,7 @@ const buildColumns = <T,>({
title: column_name,
size: 300,
id: column_name,
enableSorting: true,
header: () => {
return (
<div
@@ -33,9 +33,35 @@ const buildColumns = <T,>({
</div>
);
},
enableSorting: true,
cell: ({ row }) => {
const value = row.getValue(column_name) as any;
cell: ({ row, table, column }) => {
const initialValue = row.getValue(column_name) as any;
const [value, setValue] = useState(initialValue);
const [isEditing, setIsEditing] = useState(false);
function handleDoubleClick() {
setIsEditing(true);
}
function handleSave() {
if (value === initialValue) return;
(table.options.meta as any)?.setEditedRows((old) => ({
...old,
[row.index]: true,
}));
(table.options.meta as any)?.updateData(row.index, column.id, value);
setIsEditing(false);
}
if (isEditing) {
return (
<input
// biome-ignore lint/a11y/noAutofocus: <explanation>
autoFocus={true}
className={"w-full focus:ring"}
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={handleSave}
/>
);
}
let finalValue = value;
if (
formatDates &&
@@ -43,43 +69,18 @@ const buildColumns = <T,>({
) {
finalValue = new Date(value as string).toLocaleString();
}
if (showImagesPreview && typeof value === "string" && isUrl(value)) {
const isImage = isImageUrl(value);
return (
<a
href={value}
target={"_blank"}
className={cn("hover:underline")}
rel="noreferrer"
>
<div
className={"flex items-center justify-between break-all gap-4"}
>
{value}
{isImage && (
<img
src={value}
alt={"preview"}
className="size-20 object-cover"
/>
)}
</div>
</a>
);
if (typeof value === "string" && isUrl(value)) {
return <UrlWithPreview url={value} />;
}
if (typeof finalValue === "boolean") {
finalValue = finalValue ? "true" : "false";
}
return (
<div
className={cn(
"break-all",
["integer", "int", "tinyint", "double"].includes(data_type) &&
"text-right",
)}
>
{finalValue}
</div>
<BodyCell
value={finalValue}
dataType={data_type}
onDoubleClick={handleDoubleClick}
/>
);
},
})) as ColumnDef<T>[];
@@ -91,13 +92,11 @@ export const useColumns = ({
}: { dbName: string; tableName: string }) => {
const { data: details } = useTableColumnsQuery({ dbName, tableName });
const formatDates = useSettingsStore.use.formatDates();
const showImagesPreview = useSettingsStore.use.showImagesPreview();
return useMemo(() => {
return buildColumns({
columns: details,
formatDates,
showImagesPreview,
});
}, [details, formatDates, showImagesPreview]);
}, [details, formatDates]);
};

View File

@@ -0,0 +1,28 @@
import { cn, isImageUrl } from "@/lib/utils";
import { useSettingsStore } from "@/state";
import { memo } from "react";
const RawUrlWithPreview = ({ url }: { url: string }) => {
const showImagesPreview = useSettingsStore.use.showImagesPreview();
const isImage = showImagesPreview && isImageUrl(url);
return (
<a
href={url}
target={"_blank"}
className={cn("hover:underline")}
rel="noreferrer"
>
<div className={"flex items-center justify-between break-all gap-4"}>
{url}
{isImage && (
<img src={url} alt={"preview"} className="size-20 object-cover" />
)}
</div>
</a>
);
};
export const UrlWithPreview = memo(RawUrlWithPreview);
UrlWithPreview.displayName = "UrlWithPreview";

View File

@@ -86,6 +86,14 @@ function Component() {
sortDesc: filters.sortDesc,
});
const [tableData, setTableData] = useState<Array<Record<string, any>>>([]);
console.log(tableData);
const [editedRows, setEditedRows] = useState({});
console.log("editedRows", editedRows);
useEffect(() => {
setTableData(structuredClone(data?.data) ?? []);
}, [data]);
const handleWhereClauseFormSubmit = useCallback(
({ whereClause }: WhereClauseFormValues) => {
if (whereClause === whereQuery) {
@@ -120,9 +128,26 @@ function Component() {
);
const table = useReactTable({
meta: {
editedRows,
setEditedRows,
updateData: (rowIndex: number, columnId: string, value: string) => {
setTableData((old) =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value,
};
}
return row;
}),
);
},
},
columnResizeMode: "onChange",
columns,
data: data?.data ?? [],
data: tableData,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
manualSorting: true,

View File

@@ -42,6 +42,12 @@ export const useTablesListQuery = (args: GetTablesListArgs) => {
export const useTableDataQuery = (args: GetTableDataArgs) => {
return useQuery({
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
refetchInterval: false,
refetchIntervalInBackground: false,
staleTime: Number.POSITIVE_INFINITY,
queryKey: [DB_QUERY_KEYS.TABLES.DATA, args],
queryFn: () => dbService.getTableData(args),
placeholderData: (previousData, previousQuery) => {