fix(console): centre modals via a portal, not inside a transformed ancestor
The New Lead form opened off-centre and its backdrop covered only part of the screen. Cause: the dialog uses position:fixed, but .shell__main animates transform on entrance, and an element that has animated transform keeps acting as the containing block in Chrome even after the animation finishes. So "fixed" resolved against the 1460px max-width, margin-auto content area — offset by the sidebar — instead of the viewport. Every scrim dialog shared this latent bug. Each now renders through createPortal(…, document.body), so the fixed overlay is always relative to the viewport regardless of any transformed ancestor. New Lead, the conversation and call threads, the full lead file, the agent card, and the reasoning dialog all now centre and dim the whole screen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
adef7838fc
commit
119bea1e76
@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
import { AGENTS } from '../api/agents.js'
|
import { AGENTS } from '../api/agents.js'
|
||||||
import './AgentCard.css'
|
import './AgentCard.css'
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ export default function AgentCard({ agentKey, onClose }) {
|
|||||||
|
|
||||||
if (!a) return null
|
if (!a) return null
|
||||||
|
|
||||||
return (
|
return createPortal(
|
||||||
<div className="prose__scrim" onClick={onClose} role="presentation">
|
<div className="prose__scrim" onClick={onClose} role="presentation">
|
||||||
<div
|
<div
|
||||||
className="prose__dlg card"
|
className="prose__dlg card"
|
||||||
@ -105,5 +106,7 @@ export default function AgentCard({ agentKey, onClose }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
,
|
||||||
|
document.body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
import './Conversation.css'
|
import './Conversation.css'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,7 +61,7 @@ export default function CallTranscript({ text, name, onClose }) {
|
|||||||
}
|
}
|
||||||
}, [onClose])
|
}, [onClose])
|
||||||
|
|
||||||
return (
|
return createPortal(
|
||||||
<div className="prose__scrim" onClick={onClose} role="presentation">
|
<div className="prose__scrim" onClick={onClose} role="presentation">
|
||||||
<div
|
<div
|
||||||
className="prose__dlg conv"
|
className="prose__dlg conv"
|
||||||
@ -101,5 +102,7 @@ export default function CallTranscript({ text, name, onClose }) {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
,
|
||||||
|
document.body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
import './ClampText.css'
|
import './ClampText.css'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +51,7 @@ function ProseDialog({ title, lead, body, onClose }) {
|
|||||||
}
|
}
|
||||||
}, [onClose])
|
}, [onClose])
|
||||||
|
|
||||||
return (
|
return createPortal(
|
||||||
<div className="prose__scrim" onClick={onClose} role="presentation">
|
<div className="prose__scrim" onClick={onClose} role="presentation">
|
||||||
<div
|
<div
|
||||||
className="prose__dlg"
|
className="prose__dlg"
|
||||||
@ -77,7 +78,8 @@ function ProseDialog({ title, lead, body, onClose }) {
|
|||||||
{body.split(/\n{2,}/).map((para, i) => <p key={i}>{para}</p>)}
|
{body.split(/\n{2,}/).map((para, i) => <p key={i}>{para}</p>)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>,
|
||||||
|
document.body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
import { baseFieldId } from '../api/config.js'
|
import { baseFieldId } from '../api/config.js'
|
||||||
import './Conversation.css'
|
import './Conversation.css'
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ export default function Conversation({ rows, name, onClose }) {
|
|||||||
}
|
}
|
||||||
}, [onClose])
|
}, [onClose])
|
||||||
|
|
||||||
return (
|
return createPortal(
|
||||||
<div className="prose__scrim" onClick={onClose} role="presentation">
|
<div className="prose__scrim" onClick={onClose} role="presentation">
|
||||||
<div
|
<div
|
||||||
className="prose__dlg conv"
|
className="prose__dlg conv"
|
||||||
@ -128,5 +129,7 @@ export default function Conversation({ rows, name, onClose }) {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
,
|
||||||
|
document.body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
import ClampText from './ClampText.jsx'
|
import ClampText from './ClampText.jsx'
|
||||||
import './LeadFileDialog.css'
|
import './LeadFileDialog.css'
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ export default function LeadFileDialog({ groups, labels, label, money, fileHref,
|
|||||||
|
|
||||||
const found = shownGroups.reduce((n, [, rows]) => n + rows.length, 0)
|
const found = shownGroups.reduce((n, [, rows]) => n + rows.length, 0)
|
||||||
|
|
||||||
return (
|
return createPortal(
|
||||||
<div className="prose__scrim" onClick={onClose} role="presentation">
|
<div className="prose__scrim" onClick={onClose} role="presentation">
|
||||||
<div
|
<div
|
||||||
className="prose__dlg lfd"
|
className="prose__dlg lfd"
|
||||||
@ -159,5 +160,7 @@ export default function LeadFileDialog({ groups, labels, label, money, fileHref,
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
,
|
||||||
|
document.body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import ActivityForm from './ActivityForm.jsx'
|
import ActivityForm from './ActivityForm.jsx'
|
||||||
import { ENTRY } from '../api/config.js'
|
import { ENTRY } from '../api/config.js'
|
||||||
@ -47,7 +48,7 @@ export default function NewLeadDialog({ onClose }) {
|
|||||||
}
|
}
|
||||||
}, [onClose])
|
}, [onClose])
|
||||||
|
|
||||||
return (
|
return createPortal(
|
||||||
<div className="prose__scrim" onClick={onClose} role="presentation">
|
<div className="prose__scrim" onClick={onClose} role="presentation">
|
||||||
<div
|
<div
|
||||||
className="prose__dlg nld"
|
className="prose__dlg nld"
|
||||||
@ -105,5 +106,7 @@ export default function NewLeadDialog({ onClose }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
,
|
||||||
|
document.body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user