fix(editor): portal toolbar modals to body — container queries broke their overlays
The unified-toolbar responsiveness work gave .unified-toolbar container-type: inline-size, which makes it the containing block for position:fixed descendants. The three modals rendered inside the toolbar (library manager, install-libraries, circuit verification) shrank to the toolbar's 89px box: pinned to the top, header and close button clipped offscreen. createPortal(document.body) makes them immune to ancestor containment. The library manager also gets a fixed tall height (min(86vh, 1000px)) instead of a bouncy 80vh cap.
This commit is contained in:
parent
63a91a08ef
commit
c550b29f9e
|
|
@ -9,6 +9,7 @@
|
|||
* The component is dumb: it just renders the verification result it's
|
||||
* given. The caller (EditorToolbar) decides when to show it.
|
||||
*/
|
||||
import { createPortal } from 'react-dom';
|
||||
import type { CircuitWarning, VerificationResult } from '../../simulation/verify/circuitVerifier';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -24,7 +25,9 @@ const SEVERITY_COPY: Record<CircuitWarning['severity'], { label: string; color:
|
|||
|
||||
export const CircuitVerificationModal: React.FC<Props> = ({ result, onCancel, onRunAnyway }) => {
|
||||
const items: CircuitWarning[] = [...result.errors, ...result.warnings];
|
||||
return (
|
||||
// Portaled to <body>: see LibraryManagerModal - the toolbar's
|
||||
// container-type would otherwise shrink this fixed overlay.
|
||||
return createPortal(
|
||||
<div style={styles.overlay} onClick={onCancel}>
|
||||
<div style={styles.modal} onClick={(e) => e.stopPropagation()}>
|
||||
<h2 style={styles.title}>Circuit verification</h2>
|
||||
|
|
@ -62,7 +65,8 @@ export const CircuitVerificationModal: React.FC<Props> = ({ result, onCancel, on
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useCallback } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { installLibrary } from '../../services/libraryService';
|
||||
import { useSimulatorStore } from '../../store/useSimulatorStore';
|
||||
|
|
@ -129,7 +130,10 @@ export const InstallLibrariesModal: React.FC<InstallLibrariesModalProps> = ({
|
|||
const installedCount = items.filter((i) => i.status === 'done').length;
|
||||
const allDone = items.length > 0 && pendingCount === 0 && !running;
|
||||
|
||||
return (
|
||||
// Portaled to <body>: the toolbar ancestor has container-type
|
||||
// (container queries), which turns it into the containing block for
|
||||
// position:fixed and shrank the overlay to the toolbar's box.
|
||||
return createPortal(
|
||||
<div className="ilib-overlay" onClick={running ? undefined : onClose}>
|
||||
<div className="ilib-modal" onClick={(e) => e.stopPropagation()}>
|
||||
{/* Header */}
|
||||
|
|
@ -280,6 +284,7 @@ export const InstallLibrariesModal: React.FC<InstallLibrariesModalProps> = ({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,10 @@
|
|||
border-radius: 8px;
|
||||
width: 680px;
|
||||
max-width: 95vw;
|
||||
max-height: 80vh;
|
||||
/* Fixed tall height (not just a cap): the list is the modal's whole
|
||||
point, so give it the room instead of jumping sizes per search. */
|
||||
height: min(86vh, 1000px);
|
||||
max-height: 92vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
searchLibraries,
|
||||
|
|
@ -279,7 +280,10 @@ export const LibraryManagerModal: React.FC<LibraryManagerModalProps> = ({ isOpen
|
|||
if (!isOpen) return null;
|
||||
const browsing = !searchQuery.trim();
|
||||
|
||||
return (
|
||||
// Portaled to <body>: the toolbar ancestor has container-type
|
||||
// (container queries), which turns it into the containing block for
|
||||
// position:fixed and shrank the overlay to the toolbar's box.
|
||||
return createPortal(
|
||||
<div className="lib-modal-overlay" onClick={onClose}>
|
||||
<div className="lib-modal" onClick={(e) => e.stopPropagation()}>
|
||||
{/* Header — the pro custom-upload button injects into .lib-modal-header */}
|
||||
|
|
@ -459,6 +463,7 @@ export const LibraryManagerModal: React.FC<LibraryManagerModalProps> = ({ isOpen
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue