mixvideo-v2/apps/desktop/src/utils/modalPortal.ts

191 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Modal Portal 工具函数
* 专门处理复杂容器结构中的 Modal 渲染问题
*/
/**
* 创建 Modal 容器并确保正确的层级
*/
export const createModalContainer = (id: string = 'modal-root'): HTMLElement => {
// 检查是否已存在容器
let container = document.getElementById(id);
if (!container) {
container = document.createElement('div');
container.id = id;
// 设置容器样式,确保不受任何父级影响
container.style.cssText = `
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
z-index: 999999;
isolation: isolate;
`;
// 直接添加到 body避免复杂容器结构的影响
document.body.appendChild(container);
}
return container;
};
/**
* 清理 Modal 容器
*/
export const cleanupModalContainer = (id: string = 'modal-root'): void => {
const container = document.getElementById(id);
if (container && container.children.length === 0) {
document.body.removeChild(container);
}
};
/**
* 检测当前环境是否有复杂的容器结构
*/
export const detectComplexContainerStructure = (element?: HTMLElement): boolean => {
const targetElement = element || document.querySelector('[class*="h-screen"]') as HTMLElement;
if (!targetElement) return false;
const indicators = [
'h-screen',
'flex-col',
'overflow-y-auto',
'overflow-auto',
'overflow-scroll'
];
// 检查是否包含复杂容器结构的指示器
const hasComplexStructure = indicators.some(indicator =>
document.querySelector(`[class*="${indicator}"]`)
);
if (hasComplexStructure) {
console.log('Complex container structure detected in DOM');
}
return hasComplexStructure;
};
/**
* 应用复杂容器结构的修复
*/
export const applyComplexContainerFix = (modalElement: HTMLElement): void => {
if (!modalElement) return;
// 添加修复类
modalElement.classList.add('complex-container-fix');
modalElement.setAttribute('data-complex-container', 'true');
// 强制样式修复
const fixStyles = {
position: 'fixed',
top: '0',
left: '0',
right: '0',
bottom: '0',
width: '100vw',
height: '100vh',
zIndex: '999999',
transform: 'translateZ(0)',
isolation: 'isolate',
contain: 'none',
clipPath: 'none',
clip: 'auto'
};
Object.assign(modalElement.style, fixStyles);
// 修复背景遮罩
const backdrop = modalElement.querySelector('.modal-backdrop-fixed') as HTMLElement;
if (backdrop) {
const backdropStyles = {
position: 'fixed',
top: '0',
left: '0',
right: '0',
bottom: '0',
width: '100vw',
height: '100vh',
zIndex: '-1'
};
Object.assign(backdrop.style, backdropStyles);
}
console.log('Applied complex container fix to modal');
};
/**
* 移除复杂容器结构的修复
*/
export const removeComplexContainerFix = (modalElement: HTMLElement): void => {
if (!modalElement) return;
modalElement.classList.remove('complex-container-fix');
modalElement.removeAttribute('data-complex-container');
// 重置样式(让 CSS 类接管)
const stylesToReset = [
'position', 'top', 'left', 'right', 'bottom',
'width', 'height', 'zIndex', 'transform',
'isolation', 'contain', 'clipPath', 'clip'
];
stylesToReset.forEach(prop => {
modalElement.style.removeProperty(prop);
});
const backdrop = modalElement.querySelector('.modal-backdrop-fixed') as HTMLElement;
if (backdrop) {
stylesToReset.forEach(prop => {
backdrop.style.removeProperty(prop);
});
}
};
/**
* 获取安全的 Modal 渲染容器
* 确保 Modal 始终渲染在正确的位置
*/
export const getSafeModalContainer = (): HTMLElement => {
// 优先使用专门的 modal 容器
let container = document.getElementById('modal-root');
if (!container) {
container = createModalContainer('modal-root');
}
// 确保容器在 body 的最后,避免被其他元素覆盖
if (container.parentElement === document.body) {
document.body.appendChild(container);
}
return container;
};
/**
* 监听窗口大小变化,确保 Modal 始终正确覆盖
*/
export const setupModalResizeHandler = (modalElement: HTMLElement): (() => void) => {
const handleResize = () => {
if (modalElement.hasAttribute('data-complex-container')) {
// 重新应用修复
applyComplexContainerFix(modalElement);
}
};
window.addEventListener('resize', handleResize);
window.addEventListener('orientationchange', handleResize);
// 返回清理函数
return () => {
window.removeEventListener('resize', handleResize);
window.removeEventListener('orientationchange', handleResize);
};
};