fix(widgets): address table layout review
This commit is contained in:
5 files changed
+67
-32
No files matched your search
@@ -52,3 +52,7 @@ Each container row has an action menu, accessible via the three-dot icon or by r
|
||||
### Adding the widget
|
||||
|
||||
<AddingWidget />
|
||||
|
||||
### Configuration
|
||||
|
||||
<WidgetConfig configuration={dockerContainersWidget.configuration} />
|
||||
@@ -26,7 +26,7 @@ import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { WidgetComponentProps } from "../definition";
|
||||
import { HomarrDataTable } from "../common/homarr-data-table";
|
||||
import { usePersistedTableLayout } from "../common/use-persisted-table-layout";
|
||||
import { usePersistedTableLayout, useTableLayoutPersistence } from "../common/use-persisted-table-layout";
|
||||
import type { BeszelSystemRow } from "../beszel/_shared/types";
|
||||
import { loadAvgColor, statusColorMap, thresholdColor } from "../beszel/_shared/colors";
|
||||
import {
|
||||
@@ -116,13 +116,13 @@ export default function BeszelSystemTableWidget({
|
||||
message: t("error.layoutSaveMessage"),
|
||||
}),
|
||||
});
|
||||
const persistLayout = useCallback(
|
||||
(newOptions: Partial<Pick<typeof options, "columnOrder" | "columnWidths">>) => {
|
||||
setOptions({ newOptions });
|
||||
if (hasChangeAccess && boardId && itemId) saveItemOptions({ boardId, itemId, newOptions });
|
||||
},
|
||||
[boardId, hasChangeAccess, itemId, saveItemOptions, setOptions],
|
||||
);
|
||||
const persistLayout = useTableLayoutPersistence({
|
||||
boardId,
|
||||
hasChangeAccess,
|
||||
itemId,
|
||||
saveItemOptions,
|
||||
setOptions,
|
||||
});
|
||||
|
||||
const filteredSystems = useBeszelFilteredSystems(results, options.statusFilter);
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef } from "react";
|
||||
import type { DataTableColumn } from "mantine-datatable";
|
||||
import { useDataTableColumns } from "mantine-datatable";
|
||||
|
||||
interface TableLayoutOptions {
|
||||
interface TableLayoutOptions extends Record<string, unknown> {
|
||||
columnOrder?: string;
|
||||
columnWidths?: string;
|
||||
}
|
||||
|
||||
interface UseTableLayoutPersistenceProps {
|
||||
boardId: string | undefined;
|
||||
hasChangeAccess: boolean;
|
||||
itemId: string | undefined;
|
||||
saveItemOptions: (input: { boardId: string; itemId: string; newOptions: TableLayoutOptions }) => void;
|
||||
setOptions: (input: { newOptions: TableLayoutOptions }) => void;
|
||||
}
|
||||
|
||||
interface UsePersistedTableLayoutProps<T> {
|
||||
columns: DataTableColumn<T>[];
|
||||
columnAccessors: readonly string[];
|
||||
@@ -21,6 +29,21 @@ interface UsePersistedTableLayoutProps<T> {
|
||||
|
||||
const layoutSaveDelay = 350;
|
||||
|
||||
export const useTableLayoutPersistence = ({
|
||||
boardId,
|
||||
hasChangeAccess,
|
||||
itemId,
|
||||
saveItemOptions,
|
||||
setOptions,
|
||||
}: UseTableLayoutPersistenceProps) =>
|
||||
useCallback(
|
||||
(newOptions: TableLayoutOptions) => {
|
||||
setOptions({ newOptions });
|
||||
if (hasChangeAccess && boardId && itemId) saveItemOptions({ boardId, itemId, newOptions });
|
||||
},
|
||||
[boardId, hasChangeAccess, itemId, saveItemOptions, setOptions],
|
||||
);
|
||||
|
||||
const toPixelWidth = (width: string | number): number | undefined => {
|
||||
if (typeof width === "number" && Number.isFinite(width) && width > 0) return width;
|
||||
if (typeof width !== "string" || !width.endsWith("px")) return undefined;
|
||||
@@ -147,11 +170,22 @@ export const usePersistedTableLayout = <T>({
|
||||
|
||||
const pendingLayout = useRef<TableLayoutOptions>({});
|
||||
const saveTimeout = useRef<number | undefined>(undefined);
|
||||
const onLayoutChangeRef = useRef(onLayoutChange);
|
||||
useEffect(() => {
|
||||
onLayoutChangeRef.current = onLayoutChange;
|
||||
}, [onLayoutChange]);
|
||||
const flushPendingLayout = useCallback(() => {
|
||||
const layout = pendingLayout.current;
|
||||
pendingLayout.current = {};
|
||||
saveTimeout.current = undefined;
|
||||
if (Object.keys(layout).length > 0) onLayoutChangeRef.current(layout);
|
||||
}, []);
|
||||
useEffect(
|
||||
() => () => {
|
||||
window.clearTimeout(saveTimeout.current);
|
||||
flushPendingLayout();
|
||||
},
|
||||
[],
|
||||
[flushPendingLayout],
|
||||
);
|
||||
useEffect(() => {
|
||||
if (!hydrated.current) return;
|
||||
@@ -179,11 +213,8 @@ export const usePersistedTableLayout = <T>({
|
||||
|
||||
pendingLayout.current = { ...pendingLayout.current, ...newLayout };
|
||||
window.clearTimeout(saveTimeout.current);
|
||||
saveTimeout.current = window.setTimeout(() => {
|
||||
onLayoutChange(pendingLayout.current);
|
||||
pendingLayout.current = {};
|
||||
}, layoutSaveDelay);
|
||||
}, [columnAccessors, columnsOrder, columnsWidth, onLayoutChange, savedOrder, savedWidths, visibleAccessorSet]);
|
||||
saveTimeout.current = window.setTimeout(flushPendingLayout, layoutSaveDelay);
|
||||
}, [columnAccessors, columnsOrder, columnsWidth, flushPendingLayout, savedOrder, savedWidths, visibleAccessorSet]);
|
||||
|
||||
return { effectiveColumns, storeKey };
|
||||
};
|
||||
@@ -30,7 +30,7 @@ import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { WidgetComponentProps } from "../definition";
|
||||
import { HomarrDataTable } from "../common/homarr-data-table";
|
||||
import { usePersistedTableLayout } from "../common/use-persisted-table-layout";
|
||||
import { usePersistedTableLayout, useTableLayoutPersistence } from "../common/use-persisted-table-layout";
|
||||
|
||||
type DockerContainer = RouterOutputs["docker"]["getContainers"]["containers"][number];
|
||||
type ContainerAction = "start" | "stop" | "restart" | "remove";
|
||||
@@ -242,13 +242,13 @@ export default function DockerWidget({
|
||||
message: tWidget("error.layoutSaveMessage"),
|
||||
}),
|
||||
});
|
||||
const persistLayout = useCallback(
|
||||
(newOptions: Partial<Pick<typeof options, "columnOrder" | "columnWidths">>) => {
|
||||
setOptions({ newOptions });
|
||||
if (hasChangeAccess && boardId && itemId) saveItemOptions({ boardId, itemId, newOptions });
|
||||
},
|
||||
[boardId, hasChangeAccess, itemId, saveItemOptions, setOptions],
|
||||
);
|
||||
const persistLayout = useTableLayoutPersistence({
|
||||
boardId,
|
||||
hasChangeAccess,
|
||||
itemId,
|
||||
saveItemOptions,
|
||||
setOptions,
|
||||
});
|
||||
|
||||
const defaultDirection = options.descendingDefaultSort ? "desc" : "asc";
|
||||
const [sortStatus, setSortStatus] = useState<DataTableSortStatus<DockerContainer>>({
|
||||
|
||||
@@ -54,7 +54,7 @@ import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { WidgetComponentProps } from "../definition";
|
||||
import { HomarrDataTable } from "../common/homarr-data-table";
|
||||
import { usePersistedTableLayout } from "../common/use-persisted-table-layout";
|
||||
import { usePersistedTableLayout, useTableLayoutPersistence } from "../common/use-persisted-table-layout";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
@@ -338,13 +338,13 @@ export default function DownloadClientsWidget({
|
||||
const { mutate: saveItemOptions } = clientApi.widget.options.saveItemOptions.useMutation({
|
||||
onError: () => showErrorNotification({ title: t("errors.actionFailed"), message: t("errors.actionFailedMessage") }),
|
||||
});
|
||||
const persistOption = useCallback(
|
||||
(newOptions: Partial<Pick<typeof options, "columnOrder" | "columnWidths">>) => {
|
||||
setOptions({ newOptions });
|
||||
if (hasChangeAccess && boardId && itemId) saveItemOptions({ boardId, itemId, newOptions });
|
||||
},
|
||||
[setOptions, hasChangeAccess, boardId, itemId, saveItemOptions],
|
||||
);
|
||||
const persistOption = useTableLayoutPersistence({
|
||||
boardId,
|
||||
hasChangeAccess,
|
||||
itemId,
|
||||
saveItemOptions,
|
||||
setOptions,
|
||||
});
|
||||
|
||||
let defaultSortDirection: DataTableSortStatus<ExtendedDownloadClientItem>["direction"] = "asc";
|
||||
if (options.descendingDefaultSort) defaultSortDirection = "desc";
|
||||
|
||||
Reference in new issue
Block a user