mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-03 00:17:47 +00:00
fix(ui): make trace chevron actually collapse auto-expanded traces
The first click on a trace's chevron did nothing visible when the trace's child was the currently-selected entry. Root cause: `toggle` only checked the `expanded` set; auto-expand kept re-injecting the requestId so the row stayed open even after the user added it to `expanded` (and oscillating subsequent clicks). Replace the single `expanded` set with a tristate driven by two sets — `userExpanded` (explicitly opened) and `userCollapsed` (explicitly closed). Auto-expand only fires when the id is in neither, so a click on an auto-expanded chevron now writes the collapse intent to `userCollapsed` and the row collapses on the first click. Refs #1138, #1151
This commit is contained in:
@@ -40,30 +40,72 @@ export function LogsEntryList({
|
|||||||
density = 'cozy',
|
density = 'cozy',
|
||||||
}: LogsEntryListProps) {
|
}: LogsEntryListProps) {
|
||||||
const items = useMemo(() => deriveTraceGroups(entries), [entries]);
|
const items = useMemo(() => deriveTraceGroups(entries), [entries]);
|
||||||
const [expanded, setExpanded] = useState<Set<string>>(new Set());
|
// Two-set tristate: `userExpanded` = explicitly opened, `userCollapsed`
|
||||||
|
// = explicitly closed. Auto-expand (when selection lives inside a trace)
|
||||||
|
// only fires when the id is in neither set, so a user click on an
|
||||||
|
// auto-expanded chevron actually collapses — previously it added to
|
||||||
|
// `expanded` while auto-expand kept showing it, so no visible change.
|
||||||
|
const [userExpanded, setUserExpanded] = useState<Set<string>>(new Set());
|
||||||
|
const [userCollapsed, setUserCollapsed] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
const toggle = useCallback((requestId: string) => {
|
// Compute the auto-expand id (if any) — derived inside toggle and the
|
||||||
setExpanded((prev) => {
|
// effective set so a single source of truth drives both render and
|
||||||
const next = new Set(prev);
|
// click logic.
|
||||||
if (next.has(requestId)) next.delete(requestId);
|
const autoExpandedId = useMemo(() => {
|
||||||
else next.add(requestId);
|
if (!selectedEntryId) return null;
|
||||||
return next;
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Auto-expand the trace whose child is currently selected.
|
|
||||||
const effectiveExpanded = useMemo(() => {
|
|
||||||
if (!selectedEntryId) return expanded;
|
|
||||||
const owning = items.find(
|
const owning = items.find(
|
||||||
(it) => it.kind === 'trace' && it.children.some((c) => c.id === selectedEntryId)
|
(it) => it.kind === 'trace' && it.children.some((c) => c.id === selectedEntryId)
|
||||||
);
|
);
|
||||||
if (owning && owning.kind === 'trace' && !expanded.has(owning.requestId)) {
|
return owning && owning.kind === 'trace' ? owning.requestId : null;
|
||||||
const next = new Set(expanded);
|
}, [items, selectedEntryId]);
|
||||||
next.add(owning.requestId);
|
|
||||||
return next;
|
const toggle = useCallback(
|
||||||
|
(requestId: string) => {
|
||||||
|
const isAutoExpanded = autoExpandedId === requestId;
|
||||||
|
const isExplicitlyOpen = userExpanded.has(requestId);
|
||||||
|
const isExplicitlyClosed = userCollapsed.has(requestId);
|
||||||
|
const isCurrentlyOpen = isExplicitlyOpen || (isAutoExpanded && !isExplicitlyClosed);
|
||||||
|
|
||||||
|
if (isCurrentlyOpen) {
|
||||||
|
// Close: clear user-open, mark user-closed (beats auto-expand).
|
||||||
|
setUserExpanded((prev) => {
|
||||||
|
if (!prev.has(requestId)) return prev;
|
||||||
|
const next = new Set(prev);
|
||||||
|
next.delete(requestId);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setUserCollapsed((prev) => {
|
||||||
|
if (prev.has(requestId)) return prev;
|
||||||
|
const next = new Set(prev);
|
||||||
|
next.add(requestId);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Open: clear user-closed, mark user-open.
|
||||||
|
setUserExpanded((prev) => {
|
||||||
|
if (prev.has(requestId)) return prev;
|
||||||
|
const next = new Set(prev);
|
||||||
|
next.add(requestId);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setUserCollapsed((prev) => {
|
||||||
|
if (!prev.has(requestId)) return prev;
|
||||||
|
const next = new Set(prev);
|
||||||
|
next.delete(requestId);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[autoExpandedId, userExpanded, userCollapsed]
|
||||||
|
);
|
||||||
|
|
||||||
|
const effectiveExpanded = useMemo(() => {
|
||||||
|
const result = new Set(userExpanded);
|
||||||
|
if (autoExpandedId && !userCollapsed.has(autoExpandedId)) {
|
||||||
|
result.add(autoExpandedId);
|
||||||
}
|
}
|
||||||
return expanded;
|
return result;
|
||||||
}, [expanded, items, selectedEntryId]);
|
}, [userExpanded, userCollapsed, autoExpandedId]);
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
(_index: number, item: DerivedItem) => {
|
(_index: number, item: DerivedItem) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user