mirror of
https://github.com/chartdb/chartdb.git
synced 2025-10-23 07:11:56 +00:00
fix: resolve canvas filter tree state issues (#953)
This commit is contained in:
@@ -42,6 +42,7 @@ interface TreeViewProps<
|
|||||||
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
loadingNodeIds?: string[];
|
loadingNodeIds?: string[];
|
||||||
|
disableCache?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TreeView<
|
export function TreeView<
|
||||||
@@ -62,12 +63,14 @@ export function TreeView<
|
|||||||
renderHoverComponent,
|
renderHoverComponent,
|
||||||
renderActionsComponent,
|
renderActionsComponent,
|
||||||
loadingNodeIds,
|
loadingNodeIds,
|
||||||
|
disableCache = false,
|
||||||
}: TreeViewProps<Type, Context>) {
|
}: TreeViewProps<Type, Context>) {
|
||||||
const { expanded, loading, loadedChildren, hasMoreChildren, toggleNode } =
|
const { expanded, loading, loadedChildren, hasMoreChildren, toggleNode } =
|
||||||
useTree({
|
useTree({
|
||||||
fetchChildren,
|
fetchChildren,
|
||||||
expanded: expandedProp,
|
expanded: expandedProp,
|
||||||
setExpanded: setExpandedProp,
|
setExpanded: setExpandedProp,
|
||||||
|
disableCache,
|
||||||
});
|
});
|
||||||
const [selectedIdInternal, setSelectedIdInternal] = React.useState<
|
const [selectedIdInternal, setSelectedIdInternal] = React.useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
@@ -145,6 +148,7 @@ export function TreeView<
|
|||||||
renderHoverComponent={renderHoverComponent}
|
renderHoverComponent={renderHoverComponent}
|
||||||
renderActionsComponent={renderActionsComponent}
|
renderActionsComponent={renderActionsComponent}
|
||||||
loadingNodeIds={loadingNodeIds}
|
loadingNodeIds={loadingNodeIds}
|
||||||
|
disableCache={disableCache}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -179,6 +183,7 @@ interface TreeNodeProps<
|
|||||||
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderHoverComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
renderActionsComponent?: (node: TreeNode<Type, Context>) => ReactNode;
|
||||||
loadingNodeIds?: string[];
|
loadingNodeIds?: string[];
|
||||||
|
disableCache?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
||||||
@@ -201,11 +206,16 @@ function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
|||||||
renderHoverComponent,
|
renderHoverComponent,
|
||||||
renderActionsComponent,
|
renderActionsComponent,
|
||||||
loadingNodeIds,
|
loadingNodeIds,
|
||||||
|
disableCache = false,
|
||||||
}: TreeNodeProps<Type, Context>) {
|
}: TreeNodeProps<Type, Context>) {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const isExpanded = expanded[node.id];
|
const isExpanded = expanded[node.id];
|
||||||
const isLoading = loading[node.id];
|
const isLoading = loading[node.id];
|
||||||
const children = loadedChildren[node.id] || node.children;
|
// If cache is disabled, always use fresh node.children
|
||||||
|
// Otherwise, use cached loadedChildren if available (for async fetched data)
|
||||||
|
const children = disableCache
|
||||||
|
? node.children
|
||||||
|
: node.children || loadedChildren[node.id];
|
||||||
const isSelected = selectedId === node.id;
|
const isSelected = selectedId === node.id;
|
||||||
|
|
||||||
const IconComponent =
|
const IconComponent =
|
||||||
@@ -423,6 +433,7 @@ function TreeNode<Type extends string, Context extends Record<Type, unknown>>({
|
|||||||
renderHoverComponent={renderHoverComponent}
|
renderHoverComponent={renderHoverComponent}
|
||||||
renderActionsComponent={renderActionsComponent}
|
renderActionsComponent={renderActionsComponent}
|
||||||
loadingNodeIds={loadingNodeIds}
|
loadingNodeIds={loadingNodeIds}
|
||||||
|
disableCache={disableCache}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
@@ -28,10 +28,12 @@ export function useTree<
|
|||||||
fetchChildren,
|
fetchChildren,
|
||||||
expanded: expandedProp,
|
expanded: expandedProp,
|
||||||
setExpanded: setExpandedProp,
|
setExpanded: setExpandedProp,
|
||||||
|
disableCache = false,
|
||||||
}: {
|
}: {
|
||||||
fetchChildren?: FetchChildrenFunction<Type, Context>;
|
fetchChildren?: FetchChildrenFunction<Type, Context>;
|
||||||
expanded?: ExpandedState;
|
expanded?: ExpandedState;
|
||||||
setExpanded?: Dispatch<SetStateAction<ExpandedState>>;
|
setExpanded?: Dispatch<SetStateAction<ExpandedState>>;
|
||||||
|
disableCache?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [expandedInternal, setExpandedInternal] = useState<ExpandedState>({});
|
const [expandedInternal, setExpandedInternal] = useState<ExpandedState>({});
|
||||||
|
|
||||||
@@ -89,8 +91,8 @@ export function useTree<
|
|||||||
// Get any previously fetched children
|
// Get any previously fetched children
|
||||||
const previouslyFetchedChildren = loadedChildren[nodeId] || [];
|
const previouslyFetchedChildren = loadedChildren[nodeId] || [];
|
||||||
|
|
||||||
// If we have static children, merge them with any previously fetched children
|
// Only cache if caching is enabled
|
||||||
if (staticChildren?.length) {
|
if (!disableCache && staticChildren?.length) {
|
||||||
const mergedChildren = mergeChildren(
|
const mergedChildren = mergeChildren(
|
||||||
staticChildren,
|
staticChildren,
|
||||||
previouslyFetchedChildren
|
previouslyFetchedChildren
|
||||||
@@ -110,8 +112,8 @@ export function useTree<
|
|||||||
// Set expanded state immediately to show static/previously fetched children
|
// Set expanded state immediately to show static/previously fetched children
|
||||||
setExpanded((prev) => ({ ...prev, [nodeId]: true }));
|
setExpanded((prev) => ({ ...prev, [nodeId]: true }));
|
||||||
|
|
||||||
// If we haven't loaded dynamic children yet
|
// If we haven't loaded dynamic children yet and cache is enabled
|
||||||
if (!previouslyFetchedChildren.length) {
|
if (!disableCache && !previouslyFetchedChildren.length) {
|
||||||
setLoading((prev) => ({ ...prev, [nodeId]: true }));
|
setLoading((prev) => ({ ...prev, [nodeId]: true }));
|
||||||
try {
|
try {
|
||||||
const fetchedChildren = await fetchChildren?.(
|
const fetchedChildren = await fetchChildren?.(
|
||||||
@@ -140,7 +142,14 @@ export function useTree<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[expanded, loadedChildren, fetchChildren, mergeChildren, setExpanded]
|
[
|
||||||
|
expanded,
|
||||||
|
loadedChildren,
|
||||||
|
fetchChildren,
|
||||||
|
mergeChildren,
|
||||||
|
setExpanded,
|
||||||
|
disableCache,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -101,13 +101,32 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
|
|||||||
areas,
|
areas,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Initialize expanded state with all schemas expanded
|
// Sync expanded state with tree data changes - only expand NEW nodes
|
||||||
useMemo(() => {
|
useEffect(() => {
|
||||||
const initialExpanded: Record<string, boolean> = {};
|
setExpanded((prev) => {
|
||||||
treeData.forEach((node) => {
|
const currentNodeIds = new Set(treeData.map((n) => n.id));
|
||||||
initialExpanded[node.id] = true;
|
let hasChanges = false;
|
||||||
|
const newExpanded: Record<string, boolean> = { ...prev };
|
||||||
|
|
||||||
|
// Add any new nodes with expanded=true (preserve existing state)
|
||||||
|
treeData.forEach((node) => {
|
||||||
|
if (!(node.id in prev)) {
|
||||||
|
newExpanded[node.id] = true;
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove nodes that no longer exist (cleanup)
|
||||||
|
Object.keys(prev).forEach((id) => {
|
||||||
|
if (!currentNodeIds.has(id)) {
|
||||||
|
delete newExpanded[id];
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only update state if something actually changed (performance)
|
||||||
|
return hasChanges ? newExpanded : prev;
|
||||||
});
|
});
|
||||||
setExpanded(initialExpanded);
|
|
||||||
}, [treeData]);
|
}, [treeData]);
|
||||||
|
|
||||||
// Filter tree data based on search query
|
// Filter tree data based on search query
|
||||||
@@ -317,6 +336,7 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
|
|||||||
expanded={expanded}
|
expanded={expanded}
|
||||||
setExpanded={setExpanded}
|
setExpanded={setExpanded}
|
||||||
className="py-2"
|
className="py-2"
|
||||||
|
disableCache={true}
|
||||||
/>
|
/>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user