diff --git a/blocksuite/affine/gfx/connector/src/view/view.ts b/blocksuite/affine/gfx/connector/src/view/view.ts index 4680a8c2a2..eea605f2db 100644 --- a/blocksuite/affine/gfx/connector/src/view/view.ts +++ b/blocksuite/affine/gfx/connector/src/view/view.ts @@ -142,6 +142,8 @@ export class ConnectorElementView extends GfxElementModelView boolean; export class GridManager extends GfxExtension { @@ -280,7 +288,7 @@ export class GridManager extends GfxExtension { * @param bound * @param options */ - search( + search( bound: IBound, options?: { /** @@ -297,16 +305,16 @@ export class GridManager extends GfxExtension { */ filter?: (T | FilterFunc)[] | FilterFunc; } - ): T extends 'local'[] ? (GfxModel | GfxLocalElementModel)[] : GfxModel[]; - search( + ): BuiltInFilterModelMap[T][]; + search( bound: IBound, options: { strict?: boolean | undefined; useSet: true; filter?: (T | FilterFunc)[] | FilterFunc; } - ): T extends 'local'[] ? Set : Set; - search( + ): Set; + search( bound: IBound, options: { strict?: boolean; diff --git a/blocksuite/framework/std/src/gfx/interactivity/gfx-view-event-handler.ts b/blocksuite/framework/std/src/gfx/interactivity/gfx-view-event-handler.ts index b6a0e3f190..f131c9820a 100644 --- a/blocksuite/framework/std/src/gfx/interactivity/gfx-view-event-handler.ts +++ b/blocksuite/framework/std/src/gfx/interactivity/gfx-view-event-handler.ts @@ -66,7 +66,29 @@ export class GfxViewEventManager { .search(new Bound(x - 5, y - 5, 10, 10), { filter: ['canvas', 'local'], }) - .map(model => this.gfx.view.get(model)) as GfxElementModelView[]; + .reduce((pre, model) => { + if ( + model.includesPoint( + x, + y, + { + hitThreshold: 10, + responsePadding: [5, 5], + }, + this.gfx.std.host + ) || + ('externalBound' in model + ? model.externalBound?.isPointInBound([x, y]) + : false) + ) { + const view = this.gfx.view.get(model) as GfxElementModelView | null; + + view && pre.push(view); + } + + return pre; + }, [] as GfxElementModelView[]); + const currentStackedViews = new Set(this._hoveredElementsStack); const visited = new Set(); diff --git a/blocksuite/framework/std/src/gfx/model/surface/local-element-model.ts b/blocksuite/framework/std/src/gfx/model/surface/local-element-model.ts index aebea0caa8..69f6aceff7 100644 --- a/blocksuite/framework/std/src/gfx/model/surface/local-element-model.ts +++ b/blocksuite/framework/std/src/gfx/model/surface/local-element-model.ts @@ -13,7 +13,7 @@ import { mutex } from 'lib0'; import type { EditorHost } from '../../../view/index.js'; import type { GfxCompatibleInterface, PointTestOptions } from '../base.js'; -import type { GfxGroupModel } from '../model.js'; +import type { GfxGroupModel, GfxModel } from '../model.js'; import type { SurfaceBlockModel } from './surface-model.js'; export function prop() { @@ -61,6 +61,8 @@ export abstract class GfxLocalElementModel implements GfxCompatibleInterface { abstract readonly type: string; + creator: GfxModel | null = null; + get deserializedXYWH() { if (!this._local.has('deserializedXYWH')) { const xywh = this.xywh; diff --git a/blocksuite/framework/std/src/utils/layer.ts b/blocksuite/framework/std/src/utils/layer.ts index 397036653a..7ca5b44337 100644 --- a/blocksuite/framework/std/src/utils/layer.ts +++ b/blocksuite/framework/std/src/utils/layer.ts @@ -1,13 +1,13 @@ import type { Store } from '@blocksuite/store'; -import type { GfxLocalElementModel } from '../gfx/index.js'; import type { Layer } from '../gfx/layer.js'; import { type GfxGroupCompatibleInterface, isGfxGroupCompatibleModel, } from '../gfx/model/base.js'; -import type { GfxBlockElementModel } from '../gfx/model/gfx-block-model.js'; +import { type GfxBlockElementModel } from '../gfx/model/gfx-block-model.js'; import type { GfxModel } from '../gfx/model/model.js'; +import { GfxLocalElementModel } from '../gfx/model/surface/local-element-model.js'; import type { SurfaceBlockModel } from '../gfx/model/surface/surface-model.js'; export function getLayerEndZIndex(layers: Layer[], layerIndex: number) { @@ -90,6 +90,39 @@ export function renderableInEdgeless( return parent === doc.root || parent === surface; } +export function compareIndex(aIndex: string, bIndex: string) { + return aIndex === bIndex + ? SortOrder.SAME + : aIndex < bIndex + ? SortOrder.BEFORE + : SortOrder.AFTER; +} + +function compareLocal( + a: GfxModel | GfxLocalElementModel, + b: GfxModel | GfxLocalElementModel +) { + const isALocal = a instanceof GfxLocalElementModel; + const isBLocal = b instanceof GfxLocalElementModel; + + if (isALocal && a.creator && a.creator === b) { + return SortOrder.AFTER; + } + + if (isBLocal && b.creator && b.creator === a) { + return SortOrder.BEFORE; + } + + if (isALocal && isBLocal && a.creator && a.creator === b.creator) { + return compareIndex(a.index, b.index); + } + + return { + a: isALocal && a.creator ? a.creator : a, + b: isBLocal && b.creator ? b.creator : b, + }; +} + /** * A comparator function for sorting elements in the surface. * SortOrder.AFTER means a should be rendered after b and so on. @@ -99,6 +132,15 @@ export function compare( a: GfxModel | GfxLocalElementModel, b: GfxModel | GfxLocalElementModel ) { + const result = compareLocal(a, b); + + if (typeof result === 'number') { + return result; + } + + a = result.a; + b = result.b; + if (isGfxGroupCompatibleModel(a) && b.groups.includes(a)) { return SortOrder.BEFORE; } else if (isGfxGroupCompatibleModel(b) && a.groups.includes(b)) { @@ -128,10 +170,6 @@ export function compare( aGroup = aGroup ?? a; bGroup = bGroup ?? b; - return aGroup.index === bGroup.index - ? SortOrder.SAME - : aGroup.index < bGroup.index - ? SortOrder.BEFORE - : SortOrder.AFTER; + return compareIndex(aGroup.index, bGroup.index); } } diff --git a/tests/blocksuite/e2e/edgeless/connector/label.spec.ts b/tests/blocksuite/e2e/edgeless/connector/label.spec.ts index 2709afd2b8..6e07281a78 100644 --- a/tests/blocksuite/e2e/edgeless/connector/label.spec.ts +++ b/tests/blocksuite/e2e/edgeless/connector/label.spec.ts @@ -6,6 +6,7 @@ import { createShapeElement, dragBetweenViewCoords, edgelessCommonSetup as commonSetup, + getConnectorLabel, locatorComponentToolbar, setEdgelessTool, Shape, @@ -332,4 +333,30 @@ test.describe('connector label with straight shape', () => { await type(page, 'c'); await assertEdgelessCanvasText(page, 'c'); }); + + test('should enter the correct label', async ({ page }) => { + await commonSetup(page); + const connector1 = await addBasicConnectorElement( + page, + { x: 100, y: 200 }, + { x: 300, y: 300 } + ); + const connector2 = await addBasicConnectorElement( + page, + { x: 300, y: 200 }, + { x: 100, y: 300 } + ); + + await page.mouse.dblclick(155, 207); + await type(page, 'Connector 1'); + await page.keyboard.press('Escape'); + + expect(await getConnectorLabel(page, connector1)).toBe('Connector 1'); + + await page.mouse.dblclick(245, 207); + await type(page, 'Connector 2'); + await page.keyboard.press('Escape'); + + await expect(await getConnectorLabel(page, connector2)).toBe('Connector 2'); + }); }); diff --git a/tests/blocksuite/e2e/utils/actions/edgeless.ts b/tests/blocksuite/e2e/utils/actions/edgeless.ts index f4f7827186..d8899a616e 100644 --- a/tests/blocksuite/e2e/utils/actions/edgeless.ts +++ b/tests/blocksuite/e2e/utils/actions/edgeless.ts @@ -4,7 +4,11 @@ import { ConnectorTool } from '@blocksuite/affine/gfx/connector'; import { ShapeTool } from '@blocksuite/affine/gfx/shape'; import type { IPoint, IVec } from '@blocksuite/affine/global/gfx'; import { sleep } from '@blocksuite/affine/global/utils'; -import type { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine/model'; +import type { + ConnectorElementModel, + NoteBlockModel, + NoteDisplayMode, +} from '@blocksuite/affine/model'; import type { ToolOptions } from '@blocksuite/affine/std/gfx'; import type { Locator, Page } from '@playwright/test'; import { expect } from '@playwright/test'; @@ -418,6 +422,23 @@ export async function assertEdgelessTool(page: Page, mode: string) { expect(type).toEqual(mode); } +export async function getConnectorLabel(page: Page, id: string) { + const text = await page.evaluate(id => { + const container = document.querySelector('affine-edgeless-root'); + if (!container) { + throw new Error('Missing edgeless page'); + } + const connector = container.gfx.getElementById(id) as ConnectorElementModel; + if (!connector) { + throw new Error('Missing connector'); + } + + return connector.text?.toString() ?? ''; + }, id); + + return text; +} + export async function assertEdgelessConnectorToolMode( page: Page, mode: ConnectorMode @@ -524,6 +545,7 @@ export async function addBasicConnectorElement( ) { await setEdgelessTool(page, 'connector'); await dragBetweenCoords(page, start, end, { steps: 100 }); + return (await getSelectedIds(page))[0]; } export async function addBasicFrameElement(