diff --git a/src/web-ui/src/app/components/SceneBar/SceneBar.tsx b/src/web-ui/src/app/components/SceneBar/SceneBar.tsx index 19a188f8f3..4397e07348 100644 --- a/src/web-ui/src/app/components/SceneBar/SceneBar.tsx +++ b/src/web-ui/src/app/components/SceneBar/SceneBar.tsx @@ -250,51 +250,53 @@ const SceneBar: React.FC = ({ }; // Each tab names its device through the label that opens its group. - let groupLabel: string | undefined; - const tabItems = shownTabs.reduce((items, tab) => { - const group = groupStarts.get(tab.id); - if (group) groupLabel = `${groupLabelId}-${group.order}`; - const def = tabDefs.find(candidate => candidate.id === tab.id); - if (!def) return items; + const tabItems = useMemo(() => { + let groupLabel: string | undefined; + return shownTabs.reduce((items, tab) => { + const group = groupStarts.get(tab.id); + if (group) groupLabel = `${groupLabelId}-${group.order}`; + const def = tabDefs.find(candidate => candidate.id === tab.id); + if (!def) return items; - const translatedLabel = def.labelKey ? t(def.labelKey) : def.label; - const sessionLabel = sessionLabels[tab.id]; - const displayLabel = sessionLabel?.title || translatedLabel; - const closeLabel = t('sceneBar.closeTab', { label: sessionLabel?.number ? `${displayLabel} ${sessionLabel.number}` : displayLabel }); - const closable = isSceneTabClosable(def); + const translatedLabel = def.labelKey ? t(def.labelKey) : def.label; + const sessionLabel = sessionLabels[tab.id]; + const displayLabel = sessionLabel?.title || translatedLabel; + const closeLabel = t('sceneBar.closeTab', { label: sessionLabel?.number ? `${displayLabel} ${sessionLabel.number}` : displayLabel }); + const closable = isSceneTabClosable(def); - items.push({ - value: tab.id, - label: displayLabel, - tabProps: groupLabel ? { 'aria-describedby': groupLabel } : undefined, - labelSuffix: sessionLabel?.number || tab.pinned || (tab.contentId && (resources[tab.contentId]?.isDirty || resources[tab.contentId]?.fileMissing)) ? <> - {sessionLabel?.number && } - {tab.pinned && } - {tab.contentId && resources[tab.contentId]?.isDirty && •} - {tab.contentId && resources[tab.contentId]?.fileMissing && } - : undefined, - labelTransitionKey: tab.session?.sessionId, - // Keep the close hit target stationary between pointer down and up; - // shrinking it can retarget the click at the button edge (issue #2210). - endAction: closable ? ( - - ) : undefined, - }); - return items; - }, []); + items.push({ + value: tab.id, + label: displayLabel, + tabProps: groupLabel ? { 'aria-describedby': groupLabel } : undefined, + labelSuffix: sessionLabel?.number || tab.pinned || (tab.contentId && (resources[tab.contentId]?.isDirty || resources[tab.contentId]?.fileMissing)) ? <> + {sessionLabel?.number && } + {tab.pinned && } + {tab.contentId && resources[tab.contentId]?.isDirty && •} + {tab.contentId && resources[tab.contentId]?.fileMissing && } + : undefined, + labelTransitionKey: tab.session?.sessionId, + // Keep the close hit target stationary between pointer down and up; + // shrinking it can retarget the click at the button edge (issue #2210). + endAction: closable ? ( + + ) : undefined, + }); + return items; + }, []); + }, [closeScene, groupLabelId, groupStarts, resources, sessionLabels, shownTabs, t, tComponents, tabDefs]); return (