Files
Genarrative/src/components/image-editor/useImageCanvasFloatingOptionDismiss.ts
T
JenkenB ac180ecad9 完善画板生成音乐与生成面板交互
新增画板底部生成音乐入口,支持游戏音效和游戏背景音乐生成面板。

接入编辑器音频生成前后端契约、VectorEngine 请求与 BFF 路由。

补齐音频结果图层、元数据、画布恢复和客户端提交流程。

统一生成类面板 Lovart 式参数浮层、参考素材入口与相关测试。

更新编辑器音乐生成设计文档和项目长期记忆。
2026-06-18 15:42:17 +08:00

61 lines
1.5 KiB
TypeScript

import { useEffect } from 'react';
type FloatingOptionBoundaryRef = {
readonly current: HTMLElement | null;
};
type UseImageCanvasFloatingOptionDismissOptions = {
isOpen: boolean;
boundaryRefs: Array<FloatingOptionBoundaryRef | null | undefined>;
onDismiss: () => void;
};
function isEventInsideBoundary(
target: EventTarget | null,
boundaryRefs: Array<FloatingOptionBoundaryRef | null | undefined>,
) {
if (!(target instanceof Node)) {
return false;
}
return boundaryRefs.some((boundaryRef) => {
const element = boundaryRef?.current;
return element ? element.contains(target) : false;
});
}
function isEventInsideFloatingMenu(target: EventTarget | null) {
return (
target instanceof Element &&
target.closest('.image-canvas-editor__portal-menu')
);
}
export function useImageCanvasFloatingOptionDismiss({
isOpen,
boundaryRefs,
onDismiss,
}: UseImageCanvasFloatingOptionDismissOptions) {
useEffect(() => {
if (!isOpen || typeof document === 'undefined') {
return undefined;
}
const handleClick = (event: MouseEvent) => {
// 中文注释:选项项点击后要保留浮层;父级面板其它区域点击才收起。
if (
isEventInsideBoundary(event.target, boundaryRefs) ||
isEventInsideFloatingMenu(event.target)
) {
return;
}
onDismiss();
};
document.addEventListener('click', handleClick);
return () => {
document.removeEventListener('click', handleClick);
};
}, [boundaryRefs, isOpen, onDismiss]);
}