// Copyright (c) 2025-2026, RTE (https://www.rte-france.com) // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, // you can obtain one at http://mozilla.org/MPL/2.0/. // SPDX-License-Identifier: MPL-2.0 // This file is part of Co-Study4Grid a Power Grid Study tool Assistant Interface to help solve contigencies for a grid state under study. import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; import type { TabId } from '../types'; import { colors } from '../styles/tokens'; /** Resolve the opener's current theme ('dark' | 'light'). */ function currentTheme(): 'dark' | 'light' { return document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'; } /** * Mirror the opener's theme onto a popup document (QW19). The popup clones the * opener's stylesheets (incl. tokens.css with its `[data-theme="dark"]` * overrides), but NOT the `data-theme` attribute the theme toggle writes on the * opener's — so without this a detached tab always rendered light. */ function mirrorTheme(popupDoc: Document): void { const theme = currentTheme(); popupDoc.documentElement.setAttribute('data-theme', theme); popupDoc.documentElement.style.colorScheme = theme; } /** * A "detached" tab lives in a secondary browser window. The React component * tree for the tab content is still rendered from App.tsx — only a stable * portal-target DOM node is physically relocated (via appendChild) into the * popup's body by `DetachableTabHost`. Because the createPortal target never * changes (it's the same orphan div), React never unmounts the sub-tree — * so refs, event listeners, MemoizedSvgContainer state and the SVG viewBox * attribute all survive the detach/reattach round-trip unchanged. */ export interface DetachedTabEntry { /** The opened popup window — closed popups are pruned automatically. */ window: Window; /** The