mirror of
https://github.com/garethgeorge/backrest.git
synced 2026-09-18 22:15:34 +00:00
fix: prerendering expensive children in accordion
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
AccordionItem,
|
||||
AccordionItemContent,
|
||||
AccordionItemTrigger,
|
||||
AccordionRoot,
|
||||
} from "../ui/accordion";
|
||||
import { Text } from "@chakra-ui/react";
|
||||
|
||||
export interface HeavyAccordionItem {
|
||||
key: string;
|
||||
label: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
interface HeavyAccordionProps {
|
||||
items: HeavyAccordionItem[];
|
||||
defaultExpanded?: string[];
|
||||
}
|
||||
|
||||
export const HeavyAccordion = ({
|
||||
items,
|
||||
defaultExpanded = [],
|
||||
}: HeavyAccordionProps) => {
|
||||
const [visitedKeys, setVisitedKeys] = useState<Set<string>>(new Set());
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultExpanded.length > 0) {
|
||||
setVisitedKeys((prev) => {
|
||||
let changed = false;
|
||||
for (const k of defaultExpanded) {
|
||||
if (!prev.has(k)) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
const next = new Set(prev);
|
||||
defaultExpanded.forEach((k) => next.add(k));
|
||||
return next;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
}
|
||||
}, [defaultExpanded.join(",")]);
|
||||
|
||||
const handleAccordionChange = (e: { value: string[] }) => {
|
||||
setVisitedKeys((prev) => {
|
||||
let changed = false;
|
||||
for (const k of e.value) {
|
||||
if (!prev.has(k)) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
const next = new Set(prev);
|
||||
e.value.forEach((k) => next.add(k));
|
||||
return next;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AccordionRoot
|
||||
collapsible
|
||||
multiple
|
||||
defaultValue={defaultExpanded}
|
||||
variant="plain"
|
||||
onValueChange={handleAccordionChange}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<AccordionItem key={item.key} value={item.key} border="none">
|
||||
<AccordionItemTrigger py={2}>
|
||||
<Text fontSize="sm" fontWeight="medium">
|
||||
{item.label}
|
||||
</Text>
|
||||
</AccordionItemTrigger>
|
||||
<AccordionItemContent pb={4}>
|
||||
{visitedKeys.has(item.key) || defaultExpanded.includes(item.key)
|
||||
? item.children
|
||||
: null}
|
||||
</AccordionItemContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</AccordionRoot>
|
||||
);
|
||||
};
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
OperationRestore,
|
||||
OperationStatus,
|
||||
} from "../../../gen/ts/v1/operations_pb";
|
||||
import { HeavyAccordion } from "../../components/common/HeavyAccordion";
|
||||
import {
|
||||
Button,
|
||||
GridItem,
|
||||
@@ -28,12 +29,7 @@ import { FiFileText, FiMoreVertical, FiTrash2, FiX } from "react-icons/fi";
|
||||
import { ProgressCircle } from "../../components/ui/progress-circle";
|
||||
import { ProgressBar, ProgressRoot } from "../../components/ui/progress";
|
||||
import { toaster } from "../../components/ui/toaster";
|
||||
import {
|
||||
AccordionItem,
|
||||
AccordionItemTrigger,
|
||||
AccordionItemContent,
|
||||
AccordionRoot,
|
||||
} from "../../components/ui/accordion";
|
||||
|
||||
import {
|
||||
BackupProgressEntry,
|
||||
ResticSnapshot,
|
||||
@@ -111,6 +107,7 @@ export const OperationRow = ({
|
||||
const displayType = getTypeForDisplay(operation);
|
||||
const setRefresh = useState(0)[1];
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (operation.status === OperationStatus.STATUS_INPROGRESS) {
|
||||
const interval = setInterval(() => {
|
||||
@@ -282,6 +279,7 @@ export const OperationRow = ({
|
||||
bodyItems.push({
|
||||
key: "browser",
|
||||
label: m.op_row_snapshot_browser(),
|
||||
|
||||
children: (
|
||||
<SnapshotBrowser
|
||||
snapshotId={snapshotOp.snapshot!.id}
|
||||
@@ -385,6 +383,8 @@ export const OperationRow = ({
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="backrest visible-on-hover"
|
||||
@@ -436,25 +436,10 @@ export const OperationRow = ({
|
||||
|
||||
{bodyItems.length > 0 && (
|
||||
<Box mt={2} pl={2}>
|
||||
<AccordionRoot
|
||||
collapsible
|
||||
multiple
|
||||
defaultValue={expandedBodyItems}
|
||||
variant="plain"
|
||||
>
|
||||
{bodyItems.map((item) => (
|
||||
<AccordionItem key={item.key} value={item.key} border="none">
|
||||
<AccordionItemTrigger py={2}>
|
||||
<Text fontSize="sm" fontWeight="medium">
|
||||
{item.label}
|
||||
</Text>
|
||||
</AccordionItemTrigger>
|
||||
<AccordionItemContent pb={4}>
|
||||
{item.children}
|
||||
</AccordionItemContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</AccordionRoot>
|
||||
<HeavyAccordion
|
||||
items={bodyItems}
|
||||
defaultExpanded={expandedBodyItems}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user