Browse Source

fix(GenerationComposer): 修复分组节点内选中编辑器的定位问题

新增getAbsoluteNodePosition函数计算节点基于画布的绝对坐标,替换原有相对父节点的位置计算逻辑,确保分组内节点的编辑器能正确显示在对应位置
feature/group
huangmin 1 month ago
parent
commit
86c9ade93e
  1. 30
      src/components/__tests__/GenerationComposer.test.tsx
  2. 34
      src/components/composer/GenerationComposer.tsx

30
src/components/__tests__/GenerationComposer.test.tsx

@ -427,6 +427,36 @@ describe("GenerationComposer", () => {
expect(screen.getByPlaceholderText(/Describe what you want to generate/)).toHaveClass("h-[13.5rem]");
});
it("positions the selected node editor below grouped nodes using absolute canvas coordinates", () => {
useWorkflowStore.setState({
nodes: [
{
id: "group-node-group-1",
type: "group",
position: { x: 400, y: 200 },
data: { groupId: "group-1" },
style: { width: 500, height: 360 },
} as WorkflowNode,
{
...imageNode("img-1", true),
parentId: "group-node-group-1",
extent: "parent",
expandParent: true,
groupId: "group-1",
position: { x: 100, y: 40 },
measured: { width: 260, height: 180 },
} as WorkflowNode,
],
});
render(<GenerationComposer />);
expect(screen.getByTestId("node-inline-composer")).toHaveStyle({
left: "108px",
top: "436px",
});
});
it("keeps the selected node editor below the node when below would leave the viewport", () => {
const originalInnerHeight = window.innerHeight;
const originalInnerWidth = window.innerWidth;

34
src/components/composer/GenerationComposer.tsx

@ -892,6 +892,30 @@ function getAdapter(nodeType: NodeType | null): ComposerAdapter<WorkflowNodeData
return null;
}
function getAbsoluteNodePosition(
node: WorkflowNode,
nodes: WorkflowNode[]
): { x: number; y: number } {
const nodeById = new Map(nodes.map((item) => [item.id, item]));
const visited = new Set<string>();
const resolve = (current: WorkflowNode): { x: number; y: number } => {
if (!current.parentId || visited.has(current.id)) return current.position;
visited.add(current.id);
const parent = nodeById.get(current.parentId);
if (!parent) return current.position;
const parentPosition = resolve(parent);
return {
x: parentPosition.x + current.position.x,
y: parentPosition.y + current.position.y,
};
};
return resolve(node);
}
function deriveComposerContext(nodes: WorkflowNode[], edges: WorkflowEdge[]): ComposerContext {
const selectedNodes = nodes.filter((node) => node.selected);
if (selectedNodes.length === 0) {
@ -1702,6 +1726,10 @@ export function GenerationComposer() {
() => context.node ? getComposerNodeDimensions(context.node) : null,
[context.node]
);
const selectedNodePosition = useMemo(
() => context.node ? getAbsoluteNodePosition(context.node, nodes) : null,
[context.node, nodes]
);
const safeZoom = zoom > 0 ? zoom : 1;
const viewportWidth = typeof window === "undefined" ? Number.POSITIVE_INFINITY : window.innerWidth;
const visibleLeft = -viewportX / safeZoom;
@ -1709,15 +1737,15 @@ export function GenerationComposer() {
const composerWidth = isComposerCollapsed ? COMPOSER_COLLAPSED_WIDTH : COMPOSER_EXPANDED_WIDTH;
const composerFlowWidth = composerWidth / safeZoom;
const viewportMargin = COMPOSER_VIEWPORT_MARGIN / safeZoom;
const inlineComposerPosition = context.node && selectedNodeDimensions
const inlineComposerPosition = context.node && selectedNodeDimensions && selectedNodePosition
? (() => {
const centeredLeft = context.node.position.x + selectedNodeDimensions.width / 2 - composerFlowWidth / 2;
const centeredLeft = selectedNodePosition.x + selectedNodeDimensions.width / 2 - composerFlowWidth / 2;
const minLeft = visibleLeft + viewportMargin;
const maxLeft = visibleRight - composerFlowWidth - viewportMargin;
const left = maxLeft >= minLeft
? Math.min(Math.max(centeredLeft, minLeft), maxLeft)
: minLeft;
const belowTop = context.node.position.y + selectedNodeDimensions.height + viewportMargin;
const belowTop = selectedNodePosition.y + selectedNodeDimensions.height + viewportMargin;
return {
left,

Loading…
Cancel
Save