Route Element Call's DOM decoration through an injectable root element

Element Call writes its theme classes, background and layout attributes
straight onto document.body, and portals its modals there too. That is
only correct while it owns the page; embedded in a host application it
has to confine itself to the container it was mounted into.

Add a RootElementContext, defaulting to document.body so that the
standalone and widget builds are unaffected, and point the theme classes,
data-background, no-scroll-body and the fullscreen target at it.

Give the Modal and Toast portals an explicit container as well. Radix and
vaul both default to document.body, so without this every modal, drawer
and toast would render outside the container and lose the theme and
platform attributes set on it.

No functional change: the root element is document.body until an embedder
provides otherwise.
This commit is contained in:
Valere
2026-09-01 18:54:27 +02:00
parent 0a8c24bca8
commit 0dca5835f6
7 changed files with 61 additions and 19 deletions
+5 -3
View File
@@ -80,6 +80,7 @@ import { useTypedEventEmitter } from "../useEvents";
import { muteAllAudio$ } from "../state/MuteAllAudioModel.ts";
import { useAppBarTitle } from "../AppBar.tsx";
import { useBehavior } from "../useBehavior.ts";
import { useRootElement } from "../RootElementContext.ts";
/**
* If there already are this many participants in the call, we automatically mute
@@ -123,6 +124,7 @@ export const GroupCallView: FC<Props> = ({
null,
);
const memberships = useMatrixRTCSessionMemberships(rtcSession);
const rootElement = useRootElement();
const muteAllAudio = useBehavior(muteAllAudio$);
const leaveSoundContext = useLatest(
@@ -150,11 +152,11 @@ export const GroupCallView: FC<Props> = ({
// viewport sizes smaller than 122px width. (It is actually this exact number: 122px
// tested on different devices...)
useEffect(() => {
document.body.classList.add("no-scroll-body");
rootElement.classList.add("no-scroll-body");
return (): void => {
document.body.classList.remove("no-scroll-body");
rootElement.classList.remove("no-scroll-body");
};
}, []);
}, [rootElement]);
useEffect(() => {
window.rtcSession = rtcSession;