diff --git a/blocksuite/affine/blocks/surface-ref/src/surface-ref-block.ts b/blocksuite/affine/blocks/surface-ref/src/surface-ref-block.ts index eadbdeb16d..f93c8b5478 100644 --- a/blocksuite/affine/blocks/surface-ref/src/surface-ref-block.ts +++ b/blocksuite/affine/blocks/surface-ref/src/surface-ref-block.ts @@ -49,7 +49,7 @@ import { import type { BaseSelection, Store } from '@blocksuite/store'; import { effect, signal } from '@preact/signals-core'; import { css, html, nothing } from 'lit'; -import { query, state } from 'lit/decorators.js'; +import { query } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { guard } from 'lit/directives/guard.js'; import { styleMap } from 'lit/directives/style-map.js'; @@ -103,17 +103,12 @@ export class SurfaceRefBlockComponent extends BlockComponent { this.selection.update(() => { return [this.selection.create(BlockSelection, { blockId: this.blockId })]; }); - } + }; private _initHotkey() { const selection = this.host.selection; @@ -178,7 +173,7 @@ export class SurfaceRefBlockComponent extends BlockComponent { - if (!this._focused) return; + if (!this.selected$.value) return; addParagraph(); return true; }, @@ -260,17 +255,6 @@ export class SurfaceRefBlockComponent extends BlockComponent { - this._focused = selList.some( - sel => sel.blockId === this.blockId && sel.is(BlockSelection) - ); - }) - ); - } - private _initViewport() { const refreshViewport = () => { if (!this._referenceXYWH$.value) return; @@ -436,7 +420,6 @@ export class SurfaceRefBlockComponent extends BlockComponent ${content} @@ -488,9 +471,6 @@ export class SurfaceRefBlockComponent extends BlockComponent { + this.gfx.viewport.viewportUpdated.pipe(startWith(null)).subscribe(() => { // when viewport is updated, we should keep the overlay in the same position // to get last mouse position and convert it to model coordinates const pos = this.gfx.tool.lastMousePos$.value; diff --git a/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts index dfc1b2f092..5287214107 100644 --- a/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts +++ b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts @@ -326,6 +326,16 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin( }, { global: true } ); + + // since there is not a tool called mindmap, we need to cancel the drag when the tool is changed + this.disposables.add( + this.gfx.tool.currentToolName$.subscribe(toolName => { + // FIXME: remove the assertion after gfx tool refactor + if ((toolName as string) !== 'empty' && this.readyToDrop) { + this.draggableController.cancel(); + } + }) + ); } override render() { diff --git a/blocksuite/affine/gfx/note/src/overlay/overlay.ts b/blocksuite/affine/gfx/note/src/overlay/overlay.ts index aa379a4916..ce45f673a3 100644 --- a/blocksuite/affine/gfx/note/src/overlay/overlay.ts +++ b/blocksuite/affine/gfx/note/src/overlay/overlay.ts @@ -26,7 +26,6 @@ export class NoteOverlay extends ToolOverlay { constructor(gfx: GfxController, background: Color) { super(gfx); - this.globalAlpha = 0; this.backgroundColor = gfx.std .get(ThemeProvider) .getColorValue(background, DefaultTheme.noteBackgrounColor, true); diff --git a/blocksuite/affine/gfx/shape/src/draggable/shape-draggable.ts b/blocksuite/affine/gfx/shape/src/draggable/shape-draggable.ts index 0a06f44e96..da17df0c86 100644 --- a/blocksuite/affine/gfx/shape/src/draggable/shape-draggable.ts +++ b/blocksuite/affine/gfx/shape/src/draggable/shape-draggable.ts @@ -236,30 +236,39 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin( const locked = this.gfx.viewport.locked; const selection = this.gfx.selection; if (locked || selection.editing) return; - - if (this.readyToDrop) { - const activeIndex = shapes.findIndex( - s => s.name === this.draggingShape - ); - const nextIndex = (activeIndex + 1) % shapes.length; - const next = shapes[nextIndex]; - this.draggingShape = next.name; - - this.draggableController.cancelWithoutAnimation(); - } - - const el = this.shapeContainer.querySelector( - `.shape.${this.draggingShape}` - ) as HTMLElement; - if (!el) { - console.error('Edgeless toolbar Shape element not found'); + if ( + this.gfx.tool.dragging$.peek() && + this.gfx.tool.currentToolName$.peek() === 'shape' + ) { return; } - const { x, y } = this.gfx.tool.lastMousePos$.peek(); - const { viewport } = this.edgeless.std.get(ViewportElementProvider); - const { left, top } = viewport; - const clientPos = { x: x + left, y: y + top }; - this.draggableController.dragAndMoveTo(el, clientPos); + + const activeIndex = shapes.findIndex( + s => s.name === this.draggingShape + ); + const nextIndex = (activeIndex + 1) % shapes.length; + const next = shapes[nextIndex]; + this.draggingShape = next.name; + + if (this.readyToDrop) { + this.draggableController.cancelWithoutAnimation(); + const el = this.shapeContainer.querySelector( + `.shape.${this.draggingShape}` + ) as HTMLElement; + if (!el) { + console.error('Edgeless toolbar Shape element not found'); + return; + } + const { x, y } = this.gfx.tool.lastMousePos$.peek(); + const { viewport } = this.edgeless.std.get(ViewportElementProvider); + const { left, top } = viewport; + const clientPos = { x: x + left, y: y + top }; + this.draggableController.dragAndMoveTo(el, clientPos); + } else { + this.setEdgelessTool('shape', { + shapeName: this.draggingShape, + }); + } }, }, { global: true } diff --git a/blocksuite/affine/gfx/shape/src/shape-tool.ts b/blocksuite/affine/gfx/shape/src/shape-tool.ts index b26ef0e115..bba7e4db5a 100644 --- a/blocksuite/affine/gfx/shape/src/shape-tool.ts +++ b/blocksuite/affine/gfx/shape/src/shape-tool.ts @@ -89,7 +89,6 @@ export class ShapeTool extends BaseTool { private _hideOverlay() { if (!this._shapeOverlay) return; - this._shapeOverlay.globalAlpha = 0; (this.gfx.surfaceComponent as SurfaceBlockComponent)?.refresh(); } diff --git a/blocksuite/affine/gfx/shape/src/view.ts b/blocksuite/affine/gfx/shape/src/view.ts index 7a423322d1..e4ed7e7e84 100644 --- a/blocksuite/affine/gfx/shape/src/view.ts +++ b/blocksuite/affine/gfx/shape/src/view.ts @@ -13,9 +13,9 @@ export class ShapeElementView extends GfxElementModelView { } private _initDblClickToEdit(): void { - const edgeless = this.std.view.getBlock(this.std.store.root!.id); - this.on('dblclick', () => { + const edgeless = this.std.view.getBlock(this.std.store.root!.id); + if (edgeless && !this.model.isLocked()) { mountShapeTextEditor(this.model, edgeless); } diff --git a/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts b/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts index 4dce82296f..5e520da39a 100644 --- a/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts +++ b/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts @@ -16,7 +16,6 @@ import { openHomePage } from '@affine-test/kit/utils/load-page'; import { addCodeBlock, clickNewPageButton, - getBlockSuiteEditorTitle, type, waitForEditorLoad, } from '@affine-test/kit/utils/page-logic'; @@ -178,9 +177,6 @@ test.describe('paste in multiple blocks text selection', () => { test('paste surface-ref block to another doc as embed-linked-doc block', async ({ page, }) => { - await openHomePage(page); - await clickNewPageButton(page, 'Clipboard Test'); - await waitForEditorLoad(page); await clickEdgelessModeButton(page); const container = locateEditorContainer(page); await container.click(); @@ -205,21 +201,18 @@ test('paste surface-ref block to another doc as embed-linked-doc block', async ( await insertIntoPageButton.click(); await clickPageModeButton(page); - await page.waitForTimeout(50); + await waitForEditorLoad(page); + await container.click(); // copy surface-ref block - const surfaceRefBlock = page.locator('.affine-surface-ref'); + const surfaceRefBlock = page.locator('affine-surface-ref'); await surfaceRefBlock.click(); - await page.waitForTimeout(50); + await page.waitForSelector('affine-surface-ref .focused'); await copyByKeyboard(page); // paste to another doc - await clickNewPageButton(page); - await waitForEditorLoad(page); - const title2 = getBlockSuiteEditorTitle(page); - await title2.pressSequentially('page2'); - await page.keyboard.press('Enter'); - await page.waitForTimeout(50); + await clickNewPageButton(page, 'page2'); + await pressEnter(page); // paste the surface-ref block await pasteByKeyboard(page); diff --git a/tests/blocksuite/e2e/edgeless/element-toolbar.spec.ts b/tests/blocksuite/e2e/edgeless/element-toolbar.spec.ts index 590b912ede..c114852ba4 100644 --- a/tests/blocksuite/e2e/edgeless/element-toolbar.spec.ts +++ b/tests/blocksuite/e2e/edgeless/element-toolbar.spec.ts @@ -1,13 +1,19 @@ import { expect } from '@playwright/test'; +import { clickView } from '../utils/actions/click.js'; import { addBasicRectShapeElement, + getSelectedBoundCount, locatorComponentToolbar, resizeElementByHandle, selectNoteInEdgeless, switchEditorMode, zoomResetByKeyboard, } from '../utils/actions/edgeless.js'; +import { + pressBackspace, + selectAllBlocksByKeyboard, +} from '../utils/actions/keyboard.js'; import { enterPlaygroundRoom, initEmptyEdgelessState, @@ -87,3 +93,26 @@ test('should be hidden when resizing element', async ({ page }) => { await expect(toolbar).toBeVisible(); }); + +test('should only one tool active at the same time when using shortcut to switch tool', async ({ + page, +}) => { + await enterPlaygroundRoom(page); + await initEmptyEdgelessState(page); + await switchEditorMode(page); + await clickView(page, [0, 0]); + await selectAllBlocksByKeyboard(page); + await pressBackspace(page); + + await page.keyboard.press('s'); + await page.keyboard.press('m'); + await page.keyboard.press('n'); + + await clickView(page, [100, 100]); + await clickView(page, [0, 0]); // click on empty space to deselect the note + await selectAllBlocksByKeyboard(page); + expect( + await getSelectedBoundCount(page), + 'only a note should be created' + ).toBe(1); +}); diff --git a/tests/blocksuite/e2e/edgeless/shape.spec.ts b/tests/blocksuite/e2e/edgeless/shape.spec.ts index aa32899b8f..901bca1f0a 100644 --- a/tests/blocksuite/e2e/edgeless/shape.spec.ts +++ b/tests/blocksuite/e2e/edgeless/shape.spec.ts @@ -9,7 +9,9 @@ import { changeShapeStrokeStyle, changeShapeStrokeWidth, clickComponentToolbarMoreMenuButton, + dragBetweenViewCoords, getEdgelessSelectedRect, + getSelectedBoundCount, locatorComponentToolbar, locatorEdgelessToolButton, locatorShapeStrokeStyleButton, @@ -24,13 +26,17 @@ import { import { addBasicBrushElement, addBasicRectShapeElement, + clickView, copyByKeyboard, + dblclickView, dragBetweenCoords, enterPlaygroundRoom, focusRichText, initEmptyEdgelessState, pasteByKeyboard, + pressBackspace, pressEscape, + selectAllBlocksByKeyboard, type, waitNextFrame, } from '../utils/actions/index.js'; @@ -40,6 +46,7 @@ import { assertEdgelessNonSelectedRect, assertEdgelessSelectedRect, assertRichTexts, + assertSelectedBound, } from '../utils/asserts.js'; import { test } from '../utils/playwright.js'; @@ -739,3 +746,45 @@ test.describe('shape hit test', () => { await assertEdgelessCanvasText(page, 'hello world'); }); }); + +test('should create a shape when press s and click on canvas', async ({ + page, +}) => { + await enterPlaygroundRoom(page); + await initEmptyEdgelessState(page); + await switchEditorMode(page); + await clickView(page, [0, 0]); + await zoomResetByKeyboard(page); + await selectAllBlocksByKeyboard(page); + await pressBackspace(page); + + await page.keyboard.press('s'); + await assertEdgelessTool(page, 'shape'); + await clickView(page, [100, 100]); + await selectAllBlocksByKeyboard(page); + expect(await getSelectedBoundCount(page)).toBe(1); + await assertSelectedBound(page, [100, 100, 100, 100]); +}); + +test('shape should be editable when re-enter canvas', async ({ page }) => { + await enterPlaygroundRoom(page); + await initEmptyEdgelessState(page); + await switchEditorMode(page); + await clickView(page, [0, 0]); + await zoomResetByKeyboard(page); + await selectAllBlocksByKeyboard(page); + await pressBackspace(page); + + await page.keyboard.press('s'); + await dragBetweenViewCoords(page, [0, 0], [100, 100]); + await dblclickView(page, [50, 50]); + await type(page, 'hello'); + await expect(page.locator('edgeless-shape-text-editor')).toBeAttached(); + await assertEdgelessCanvasText(page, 'hello'); + + await switchEditorMode(page); + await switchEditorMode(page); + + await dblclickView(page, [50, 50]); + await expect(page.locator('edgeless-shape-text-editor')).toBeAttached(); +});