mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-04 19:15:33 +08:00
fix(editor): connector not added as frame child (#12611)
Co-authored-by: L-Sun <zover.v@gmail.com>
This commit is contained in:
@@ -241,20 +241,35 @@ export class EdgelessFrameManager extends GfxExtension {
|
||||
surfaceModel.elementAdded.subscribe(({ id, local }) => {
|
||||
const element = surfaceModel.getElementById(id);
|
||||
if (element && local) {
|
||||
const frame = this.getFrameFromPoint(element.elementBound.center);
|
||||
|
||||
// if the container created with a frame, skip it.
|
||||
if (
|
||||
isGfxGroupCompatibleModel(element) &&
|
||||
frame &&
|
||||
element.hasChild(frame)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// new element may intended to be added to other group
|
||||
// so we need to wait for the next microtask to check if the element can be added to the frame
|
||||
// The entire frame detection logic must be in microtask for timing reasons:
|
||||
//
|
||||
// 1. For connectors: When elementAdded fires, connectors have invalid bounds [0,0,0,0]
|
||||
// because their path/bounds are calculated in a separate microtask of updateConnectorPath by connector-watcher.
|
||||
// We need to wait for that calculation to complete before frame detection.
|
||||
//
|
||||
// 2. For shapes: Although they have valid bounds immediately, processing them in microtask
|
||||
// ensures consistent timing and allows other initialization to complete first.
|
||||
//
|
||||
// 3. Group compatibility: Some elements may need to establish their group relationships
|
||||
// before being considered for frame membership.
|
||||
//
|
||||
// By embedding the entire logic in microtask, we ensure:
|
||||
// - Connectors have proper bounds calculated (not [0,0,0,0])
|
||||
// - getFrameFromPoint() works correctly with valid element centers
|
||||
// - All element initialization is complete before frame detection
|
||||
queueMicrotask(() => {
|
||||
const frame = this.getFrameFromPoint(element.elementBound.center);
|
||||
|
||||
// if the container created with a frame, skip it.
|
||||
if (
|
||||
isGfxGroupCompatibleModel(element) &&
|
||||
frame &&
|
||||
element.hasChild(frame)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only add elements that aren't already grouped and have a valid frame
|
||||
if (!element.group && frame) {
|
||||
this.addElementsToFrame(frame, [element]);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ describe('basic', () => {
|
||||
xywh: '[100, 0, 100, 100]',
|
||||
index: service.generateIndex(),
|
||||
})!;
|
||||
await wait(0); // wait next frame
|
||||
frameId = service.crud.addBlock(
|
||||
'affine:frame',
|
||||
{
|
||||
|
||||
@@ -478,3 +478,46 @@ test('undo/redo should work when change frame background', async ({ page }) => {
|
||||
expect(await getFrameBackground()).not.toBe(prevBackground);
|
||||
}
|
||||
});
|
||||
|
||||
test('connector and shape created simultaneously with edgeless-auto-complete should both be added to frame', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Create a larger frame to ensure everything fits
|
||||
const frameId = await createFrame(page, [50, 50], [650, 650]);
|
||||
|
||||
// Create first shape inside the frame (well within bounds)
|
||||
const shape1Id = await createShapeElement(
|
||||
page,
|
||||
[150, 150],
|
||||
[250, 250],
|
||||
Shape.Square
|
||||
);
|
||||
|
||||
// Click on the existing shape to start connection
|
||||
await clickView(page, [200, 200]);
|
||||
|
||||
const autoComplete = page.locator('edgeless-auto-complete');
|
||||
const rightArrowButton = autoComplete
|
||||
.locator('.edgeless-auto-complete-arrow')
|
||||
.nth(0);
|
||||
|
||||
await rightArrowButton.click();
|
||||
|
||||
// Wait for async processing
|
||||
await waitNextFrame(page);
|
||||
|
||||
// Verify that the frame contains 3 children: original shape + new shape + connector
|
||||
await assertContainerChildCount(page, frameId, 3);
|
||||
|
||||
// Verify by moving the frame - all elements should move together
|
||||
const frameTitle = page.locator('affine-frame-title');
|
||||
await frameTitle.click();
|
||||
await dragBetweenViewCoords(page, [60, 60], [110, 110]);
|
||||
|
||||
// Check that the original shape moved with the frame
|
||||
await assertEdgelessElementBound(page, shape1Id, [200, 200, 100, 100]);
|
||||
await assertEdgelessElementBound(page, frameId, [100, 100, 600, 600]);
|
||||
|
||||
// Frame should still contain all 3 elements after the move
|
||||
await assertContainerChildCount(page, frameId, 3);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user