fix: Dropdown menus rendering outside SPA container

- Add getPortalContainer to DropdownMenuContent (like Dialog)
- Portal container created inside #woonoow-admin-app
- Copy theme class (light/dark) for proper CSS variable inheritance
- Fixes Add Section dropdown styling in Page Editor
This commit is contained in:
Dwindi Ramadhana
2026-01-11 23:56:30 +07:00
parent c5b572b2c2
commit 8e53a9d65b

View File

@@ -57,20 +57,46 @@ DropdownMenuSubContent.displayName =
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
))
>(({ className, sideOffset = 4, ...props }, ref) => {
// Get or create portal container inside the app for proper CSS scoping
const getPortalContainer = () => {
const appContainer = document.getElementById('woonoow-admin-app');
if (!appContainer) return document.body;
let portalRoot = document.getElementById('woonoow-dropdown-portal');
if (!portalRoot) {
portalRoot = document.createElement('div');
portalRoot.id = 'woonoow-dropdown-portal';
// Copy theme class from documentElement for proper CSS variable inheritance
const themeClass = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
portalRoot.className = themeClass;
appContainer.appendChild(portalRoot);
} else {
// Update theme class in case it changed
const themeClass = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
if (!portalRoot.classList.contains(themeClass)) {
portalRoot.classList.remove('light', 'dark');
portalRoot.classList.add(themeClass);
}
}
return portalRoot;
};
return (
<DropdownMenuPrimitive.Portal container={getPortalContainer()}>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
);
})
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef<