// 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 React, { useCallback, type CSSProperties, type Ref } from 'react'; import type { ActionDetail, MetadataIndex } from '../types'; import ActionCard from './ActionCard'; import { colors } from '../styles/tokens'; /** * Floating ActionCard popover — shared between the action * overview view (single-click on a pin) and any future callsite * that needs a read-only-ish "peek" at an action's details * without opening the full drill-down diagram. * * The popover intentionally reuses the SAME `ActionCard` * component that the sidebar feed renders, so card visuals, * severity palette, badge resolution and tooltip text all stay * in lock-step with the feed — we only need to change the * component in ONE place. Re-simulate / edit controls are wired * to no-op stubs because the popover is a preview, not an * editor; callers that need those interactions should open the * drill-down view (which is what a double-click on the pin * already does). * * The chrome (floating card frame + close button) is owned by * this component so every callsite gets identical affordances. */ interface ActionCardPopoverProps { actionId: string; details: ActionDetail; /** Visual index shown in the card header (e.g. "#3"). */ index: number; /** * Absolute-positioning style (including `position: fixed`, * top/bottom/left/right anchors). Typically produced by * `utils/popoverPlacement.computePopoverStyle` so the * popover lands next to the anchor element. */ style: CSSProperties; /** Overloaded lines used for the "Loading after" rendering. */ linesOverloaded: readonly string[]; /** Monitoring factor — drives the severity badge. */ monitoringFactor: number; /** N-1 metadata index used by badge resolution. */ metaIndex: MetadataIndex | null; /** Selected-action set (for the card's `isSelected` styling). */ selectedActionIds?: Set; /** Rejected-action set (for the card's `isRejected` styling). */ rejectedActionIds?: Set; /** * Called when the user activates the card (clicks the card * body or the dedicated "view" affordance). Parent typically * closes the popover and switches to the action drill-down * view. */ onActivateAction: (actionId: string) => void; /** Toggle favourite status. */ onActionFavorite?: (actionId: string) => void; /** Reject the action. */ onActionReject?: (actionId: string) => void; /** Close the popover. */ onClose: () => void; /** * Optional ref on the popover root so the parent can detect * outside clicks. */ popoverRef?: Ref; /** * Optional test id on the popover root (defaults to * "action-card-popover"). Kept overridable so multiple * callsites can disambiguate their popovers in tests. */ testId?: string; /** * Optional data-attributes to forward onto the popover root — * used by `ActionOverviewDiagram` to record the placement * decision on the DOM for its unit tests. */ extraDataAttributes?: Record; /** Resolve an element/VL ID to its human-readable display name. */ displayName?: (id: string) => string; } const NOOP = () => { /* intentional no-op */ }; const ActionCardPopover: React.FC = ({ actionId, details, index, style, linesOverloaded, monitoringFactor, metaIndex, selectedActionIds, rejectedActionIds, onActivateAction, onActionFavorite, onActionReject, onClose, popoverRef, testId = 'action-card-popover', extraDataAttributes, displayName, }) => { // Wrap the "activate the action" intent so both the card // body click and any future explicit affordance flow through // the same path — and the popover always auto-dismisses. const handleActivate = useCallback((id: string | null) => { onClose(); if (id) onActivateAction(id); }, [onActivateAction, onClose]); return (
e.stopPropagation()} >
); }; export default React.memo(ActionCardPopover);