From 1f0fc9d47ac876add7f6d83e84d4e47427b0559e Mon Sep 17 00:00:00 2001 From: Saul-Mirone Date: Sat, 22 Mar 2025 14:39:05 +0000 Subject: [PATCH] feat(editor): gfx connector package (#11091) --- blocksuite/affine/all/package.json | 6 +- blocksuite/affine/all/src/gfx/connector.ts | 1 + blocksuite/affine/all/tsconfig.json | 1 + .../affine/blocks/block-root/package.json | 1 + .../src/edgeless/components/toolbar/tools.ts | 10 +-- .../src/edgeless/configs/toolbar/index.ts | 4 +- .../src/edgeless/edgeless-builtin-spec.ts | 2 +- .../src/edgeless/edgeless-keyboard.ts | 2 +- .../src/edgeless/gfx-tool/default-tool.ts | 6 +- .../block-root/src/edgeless/gfx-tool/index.ts | 1 - .../block-root/src/edgeless/utils/text.ts | 64 +----------------- .../affine/blocks/block-root/src/effects.ts | 34 +--------- .../affine/blocks/block-root/tsconfig.json | 1 + blocksuite/affine/gfx/connector/package.json | 45 +++++++++++++ .../src/components}/connector-handle.ts | 33 +++++----- .../connector/src}/connector-tool.ts | 2 + .../affine/gfx/connector/src/effects.ts | 26 ++++++++ blocksuite/affine/gfx/connector/src/index.ts | 4 ++ .../text/edgeless-connector-label-editor.ts | 0 .../affine/gfx/connector/src/text/index.ts | 1 + .../affine/gfx/connector/src/text/text.ts | 65 +++++++++++++++++++ .../connector/src/toolbar/config.ts} | 4 +- .../src/toolbar}/connector-dense-menu.ts | 0 .../connector/src/toolbar}/connector-menu.ts | 0 .../src/toolbar}/connector-tool-button.ts | 0 .../gfx/connector/src/toolbar/quick-tool.ts | 14 ++++ blocksuite/affine/gfx/connector/tsconfig.json | 20 ++++++ .../note/src/components}/icons.ts | 0 .../components}/note-display-mode-panel.ts | 0 .../note/src/components}/note-shadow-panel.ts | 0 blocksuite/affine/gfx/note/src/effects.ts | 6 ++ .../shape/src/components}/shape-panel.ts | 7 +- .../src/components}/shape-style-panel.ts | 0 blocksuite/affine/gfx/shape/src/effects.ts | 6 ++ .../e2e/utils/declare-test-window.ts | 7 +- tools/utils/src/workspace.gen.ts | 18 +++++ tsconfig.json | 1 + yarn.lock | 29 +++++++++ 38 files changed, 284 insertions(+), 137 deletions(-) create mode 100644 blocksuite/affine/all/src/gfx/connector.ts create mode 100644 blocksuite/affine/gfx/connector/package.json rename blocksuite/affine/{blocks/block-root/src/edgeless/components/connector => gfx/connector/src/components}/connector-handle.ts (86%) rename blocksuite/affine/{blocks/block-root/src/edgeless/gfx-tool => gfx/connector/src}/connector-tool.ts (97%) create mode 100644 blocksuite/affine/gfx/connector/src/effects.ts create mode 100644 blocksuite/affine/gfx/connector/src/index.ts rename blocksuite/affine/{blocks/block-root/src/edgeless/components => gfx/connector/src}/text/edgeless-connector-label-editor.ts (100%) create mode 100644 blocksuite/affine/gfx/connector/src/text/index.ts create mode 100644 blocksuite/affine/gfx/connector/src/text/text.ts rename blocksuite/affine/{blocks/block-root/src/edgeless/configs/toolbar/connector.ts => gfx/connector/src/toolbar/config.ts} (99%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/connector => gfx/connector/src/toolbar}/connector-dense-menu.ts (100%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/connector => gfx/connector/src/toolbar}/connector-menu.ts (100%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/connector => gfx/connector/src/toolbar}/connector-tool-button.ts (100%) create mode 100644 blocksuite/affine/gfx/connector/src/toolbar/quick-tool.ts create mode 100644 blocksuite/affine/gfx/connector/tsconfig.json rename blocksuite/affine/{blocks/block-root/src/edgeless/components/panel => gfx/note/src/components}/icons.ts (100%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/panel => gfx/note/src/components}/note-display-mode-panel.ts (100%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/panel => gfx/note/src/components}/note-shadow-panel.ts (100%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/panel => gfx/shape/src/components}/shape-panel.ts (93%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/panel => gfx/shape/src/components}/shape-style-panel.ts (100%) diff --git a/blocksuite/affine/all/package.json b/blocksuite/affine/all/package.json index b0d2956dbb..c4c7c8278e 100644 --- a/blocksuite/affine/all/package.json +++ b/blocksuite/affine/all/package.json @@ -33,6 +33,7 @@ "@blocksuite/affine-fragment-doc-title": "workspace:*", "@blocksuite/affine-fragment-frame-panel": "workspace:*", "@blocksuite/affine-fragment-outline": "workspace:*", + "@blocksuite/affine-gfx-connector": "workspace:*", "@blocksuite/affine-gfx-note": "workspace:*", "@blocksuite/affine-gfx-shape": "workspace:*", "@blocksuite/affine-gfx-text": "workspace:*", @@ -116,8 +117,9 @@ "./fragments/frame-panel": "./src/fragments/frame-panel.ts", "./fragments/outline": "./src/fragments/outline.ts", "./gfx/text": "./src/gfx/text.ts", - "./gfx/shape": "./src/gfx/shape/index.ts", - "./gfx/note": "./src/gfx/note/index.ts", + "./gfx/shape": "./src/gfx/shape.ts", + "./gfx/note": "./src/gfx/note.ts", + "./gfx/connector": "./src/gfx/connector.ts", "./gfx/turbo-renderer": "./src/gfx/turbo-renderer.ts", "./components/block-selection": "./src/components/block-selection.ts", "./components/block-zero-width": "./src/components/block-zero-width.ts", diff --git a/blocksuite/affine/all/src/gfx/connector.ts b/blocksuite/affine/all/src/gfx/connector.ts new file mode 100644 index 0000000000..c3a934bb4c --- /dev/null +++ b/blocksuite/affine/all/src/gfx/connector.ts @@ -0,0 +1 @@ +export * from '@blocksuite/affine-gfx-connector'; diff --git a/blocksuite/affine/all/tsconfig.json b/blocksuite/affine/all/tsconfig.json index 473b609f77..03984338a2 100644 --- a/blocksuite/affine/all/tsconfig.json +++ b/blocksuite/affine/all/tsconfig.json @@ -30,6 +30,7 @@ { "path": "../fragments/fragment-doc-title" }, { "path": "../fragments/fragment-frame-panel" }, { "path": "../fragments/fragment-outline" }, + { "path": "../gfx/connector" }, { "path": "../gfx/note" }, { "path": "../gfx/shape" }, { "path": "../gfx/text" }, diff --git a/blocksuite/affine/blocks/block-root/package.json b/blocksuite/affine/blocks/block-root/package.json index 3e73f1ff22..ee35918687 100644 --- a/blocksuite/affine/blocks/block-root/package.json +++ b/blocksuite/affine/blocks/block-root/package.json @@ -27,6 +27,7 @@ "@blocksuite/affine-block-table": "workspace:*", "@blocksuite/affine-components": "workspace:*", "@blocksuite/affine-fragment-doc-title": "workspace:*", + "@blocksuite/affine-gfx-connector": "workspace:*", "@blocksuite/affine-gfx-note": "workspace:*", "@blocksuite/affine-gfx-shape": "workspace:*", "@blocksuite/affine-gfx-text": "workspace:*", diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/tools.ts b/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/tools.ts index a4a18daf09..2460150c98 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/tools.ts +++ b/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/tools.ts @@ -1,4 +1,5 @@ import { frameQuickTool } from '@blocksuite/affine-block-frame'; +import { connectorQuickTool } from '@blocksuite/affine-gfx-connector'; import { noteSeniorTool } from '@blocksuite/affine-gfx-note'; import { shapeSeniorTool } from '@blocksuite/affine-gfx-shape'; import { @@ -18,15 +19,6 @@ const defaultQuickTool = QuickToolExtension('default', ({ block }) => { }; }); -const connectorQuickTool = QuickToolExtension('connector', ({ block }) => { - return { - type: 'connector', - content: html``, - }; -}); - const linkQuickTool = QuickToolExtension('link', ({ block, gfx }) => { return { content: html` { - editor.inlineEditor?.focusEnd(); - }) - .catch(console.error); -} diff --git a/blocksuite/affine/blocks/block-root/src/effects.ts b/blocksuite/affine/blocks/block-root/src/effects.ts index bf350f2a82..f7f045ff3b 100644 --- a/blocksuite/affine/blocks/block-root/src/effects.ts +++ b/blocksuite/affine/blocks/block-root/src/effects.ts @@ -1,3 +1,4 @@ +import { effects as gfxConnectorEffects } from '@blocksuite/affine-gfx-connector/effects'; import { effects as gfxNoteEffects } from '@blocksuite/affine-gfx-note/effects'; import { effects as gfxShapeEffects } from '@blocksuite/affine-gfx-shape/effects'; import { effects as gfxCanvasTextEffects } from '@blocksuite/affine-gfx-text/effects'; @@ -5,18 +6,13 @@ import { effects as widgetEdgelessToolbarEffects } from '@blocksuite/affine-widg import { EdgelessAutoCompletePanel } from './edgeless/components/auto-complete/auto-complete-panel.js'; import { EdgelessAutoComplete } from './edgeless/components/auto-complete/edgeless-auto-complete.js'; -import { EdgelessConnectorHandle } from './edgeless/components/connector/connector-handle.js'; import { NOTE_SLICER_WIDGET, NoteSlicer, } from './edgeless/components/note-slicer/index.js'; import { EdgelessFontFamilyPanel } from './edgeless/components/panel/font-family-panel.js'; import { EdgelessFontWeightAndStylePanel } from './edgeless/components/panel/font-weight-and-style-panel.js'; -import { NoteDisplayModePanel } from './edgeless/components/panel/note-display-mode-panel.js'; -import { EdgelessNoteShadowPanel } from './edgeless/components/panel/note-shadow-panel.js'; import { EdgelessScalePanel } from './edgeless/components/panel/scale-panel.js'; -import { EdgelessShapePanel } from './edgeless/components/panel/shape-panel.js'; -import { EdgelessShapeStylePanel } from './edgeless/components/panel/shape-style-panel.js'; import { EdgelessSizePanel } from './edgeless/components/panel/size-panel.js'; import { StrokeStylePanel } from './edgeless/components/panel/stroke-style-panel.js'; import { @@ -27,14 +23,11 @@ import { EDGELESS_SELECTED_RECT_WIDGET, EdgelessSelectedRectWidget, } from './edgeless/components/rects/edgeless-selected-rect.js'; -import { EdgelessConnectorLabelEditor } from './edgeless/components/text/edgeless-connector-label-editor.js'; import { EdgelessGroupTitleEditor } from './edgeless/components/text/edgeless-group-title-editor.js'; import { EdgelessBrushMenu } from './edgeless/components/toolbar/brush/brush-menu.js'; import { EdgelessBrushToolButton } from './edgeless/components/toolbar/brush/brush-tool-button.js'; import { EdgelessSlideMenu } from './edgeless/components/toolbar/common/slide-menu.js'; import { ToolbarArrowUpIcon } from './edgeless/components/toolbar/common/toolbar-arrow-up-icon.js'; -import { EdgelessConnectorMenu } from './edgeless/components/toolbar/connector/connector-menu.js'; -import { EdgelessConnectorToolButton } from './edgeless/components/toolbar/connector/connector-tool-button.js'; import { EdgelessDefaultToolButton } from './edgeless/components/toolbar/default/default-tool-button.js'; import { EdgelessEraserToolButton } from './edgeless/components/toolbar/eraser/eraser-tool-button.js'; import { EdgelessLassoToolButton } from './edgeless/components/toolbar/lasso/lasso-tool-button.js'; @@ -114,6 +107,7 @@ function registerGfxEffects() { gfxCanvasTextEffects(); gfxShapeEffects(); gfxNoteEffects(); + gfxConnectorEffects(); } function registerWidgets() { @@ -137,10 +131,6 @@ function registerWidgets() { function registerEdgelessToolbarComponents() { // Tool buttons customElements.define('edgeless-brush-tool-button', EdgelessBrushToolButton); - customElements.define( - 'edgeless-connector-tool-button', - EdgelessConnectorToolButton - ); customElements.define( 'edgeless-default-tool-button', EdgelessDefaultToolButton @@ -159,7 +149,6 @@ function registerEdgelessToolbarComponents() { // Menus customElements.define('edgeless-brush-menu', EdgelessBrushMenu); - customElements.define('edgeless-connector-menu', EdgelessConnectorMenu); customElements.define('edgeless-mindmap-menu', EdgelessMindmapMenu); customElements.define('edgeless-slide-menu', EdgelessSlideMenu); @@ -172,21 +161,13 @@ function registerEdgelessPanelComponents() { 'edgeless-font-weight-and-style-panel', EdgelessFontWeightAndStylePanel ); - customElements.define('edgeless-note-shadow-panel', EdgelessNoteShadowPanel); customElements.define('edgeless-size-panel', EdgelessSizePanel); customElements.define('edgeless-scale-panel', EdgelessScalePanel); customElements.define('edgeless-font-family-panel', EdgelessFontFamilyPanel); - customElements.define('edgeless-shape-panel', EdgelessShapePanel); - customElements.define('note-display-mode-panel', NoteDisplayModePanel); customElements.define('stroke-style-panel', StrokeStylePanel); - customElements.define('edgeless-shape-style-panel', EdgelessShapeStylePanel); } function registerEdgelessEditorComponents() { - customElements.define( - 'edgeless-connector-label-editor', - EdgelessConnectorLabelEditor - ); customElements.define( 'edgeless-group-title-editor', EdgelessGroupTitleEditor @@ -236,9 +217,6 @@ function registerMiscComponents() { // Mindmap components customElements.define('mindmap-import-placeholder', MindMapPlaceholder); - - // Connector components - customElements.define('edgeless-connector-handle', EdgelessConnectorHandle); } declare global { @@ -247,28 +225,20 @@ declare global { 'affine-edgeless-root-preview': EdgelessRootPreviewBlockComponent; 'edgeless-auto-complete-panel': EdgelessAutoCompletePanel; 'edgeless-auto-complete': EdgelessAutoComplete; - 'edgeless-connector-handle': EdgelessConnectorHandle; 'note-slicer': NoteSlicer; 'edgeless-font-family-panel': EdgelessFontFamilyPanel; 'edgeless-font-weight-and-style-panel': EdgelessFontWeightAndStylePanel; - 'note-display-mode-panel': NoteDisplayModePanel; - 'edgeless-note-shadow-panel': EdgelessNoteShadowPanel; 'edgeless-scale-panel': EdgelessScalePanel; - 'edgeless-shape-panel': EdgelessShapePanel; - 'edgeless-shape-style-panel': EdgelessShapeStylePanel; 'edgeless-size-panel': EdgelessSizePanel; 'stroke-style-panel': StrokeStylePanel; 'edgeless-navigator-black-background': EdgelessNavigatorBlackBackgroundWidget; 'edgeless-dragging-area-rect': EdgelessDraggingAreaRectWidget; 'edgeless-selected-rect': EdgelessSelectedRectWidget; - 'edgeless-connector-label-editor': EdgelessConnectorLabelEditor; 'edgeless-group-title-editor': EdgelessGroupTitleEditor; 'edgeless-brush-menu': EdgelessBrushMenu; 'edgeless-brush-tool-button': EdgelessBrushToolButton; 'edgeless-slide-menu': EdgelessSlideMenu; 'toolbar-arrow-up-icon': ToolbarArrowUpIcon; - 'edgeless-connector-menu': EdgelessConnectorMenu; - 'edgeless-connector-tool-button': EdgelessConnectorToolButton; 'edgeless-default-tool-button': EdgelessDefaultToolButton; 'edgeless-eraser-tool-button': EdgelessEraserToolButton; 'edgeless-lasso-tool-button': EdgelessLassoToolButton; diff --git a/blocksuite/affine/blocks/block-root/tsconfig.json b/blocksuite/affine/blocks/block-root/tsconfig.json index dae0a4db75..c7945fe85d 100644 --- a/blocksuite/affine/blocks/block-root/tsconfig.json +++ b/blocksuite/affine/blocks/block-root/tsconfig.json @@ -24,6 +24,7 @@ { "path": "../block-table" }, { "path": "../../components" }, { "path": "../../fragments/fragment-doc-title" }, + { "path": "../../gfx/connector" }, { "path": "../../gfx/note" }, { "path": "../../gfx/shape" }, { "path": "../../gfx/text" }, diff --git a/blocksuite/affine/gfx/connector/package.json b/blocksuite/affine/gfx/connector/package.json new file mode 100644 index 0000000000..011260d4ba --- /dev/null +++ b/blocksuite/affine/gfx/connector/package.json @@ -0,0 +1,45 @@ +{ + "name": "@blocksuite/affine-gfx-connector", + "description": "Gfx connector for BlockSuite.", + "type": "module", + "scripts": { + "build": "tsc" + }, + "sideEffects": false, + "keywords": [], + "author": "toeverything", + "license": "MIT", + "dependencies": { + "@blocksuite/affine-block-surface": "workspace:*", + "@blocksuite/affine-components": "workspace:*", + "@blocksuite/affine-model": "workspace:*", + "@blocksuite/affine-rich-text": "workspace:*", + "@blocksuite/affine-shared": "workspace:*", + "@blocksuite/affine-widget-edgeless-toolbar": "workspace:*", + "@blocksuite/block-std": "workspace:*", + "@blocksuite/global": "workspace:*", + "@blocksuite/icons": "^2.2.6", + "@blocksuite/store": "workspace:*", + "@lit/context": "^1.1.2", + "@preact/signals-core": "^1.8.0", + "@toeverything/theme": "^1.1.12", + "@types/lodash-es": "^4.17.12", + "lit": "^3.2.0", + "lodash-es": "^4.17.21", + "minimatch": "^10.0.1", + "rxjs": "^7.8.1", + "yjs": "^13.6.21", + "zod": "^3.23.8" + }, + "exports": { + ".": "./src/index.ts", + "./effects": "./src/effects.ts" + }, + "files": [ + "src", + "dist", + "!src/__tests__", + "!dist/__tests__" + ], + "version": "0.20.0" +} diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/connector/connector-handle.ts b/blocksuite/affine/gfx/connector/src/components/connector-handle.ts similarity index 86% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/connector/connector-handle.ts rename to blocksuite/affine/gfx/connector/src/components/connector-handle.ts index 2e31bbe373..d9f967c928 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/components/connector/connector-handle.ts +++ b/blocksuite/affine/gfx/connector/src/components/connector-handle.ts @@ -1,9 +1,11 @@ import { type ConnectionOverlay, + EdgelessLegacySlotIdentifier, OverlayIdentifier, } from '@blocksuite/affine-block-surface'; import type { ConnectorElementModel } from '@blocksuite/affine-model'; import { + type BlockComponent, type BlockStdScope, docContext, stdContext, @@ -18,8 +20,6 @@ import { css, html, LitElement } from 'lit'; import { property, query } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; -import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js'; - const SIZE = 12; const HALF_SIZE = SIZE / 2; @@ -59,15 +59,19 @@ export class EdgelessConnectorHandle extends WithDisposable(LitElement) { return this.std.get(GfxControllerIdentifier); } + get slots() { + return this.std.get(EdgelessLegacySlotIdentifier); + } + private _bindEvent() { - const edgeless = this.edgeless; + const slots = this.slots; this._disposables.addFromEvent(this._startHandler, 'pointerdown', e => { - edgeless.slots.elementResizeStart.next(); + slots.elementResizeStart.next(); this._capPointerDown(e, 'source'); }); this._disposables.addFromEvent(this._endHandler, 'pointerdown', e => { - edgeless.slots.elementResizeStart.next(); + slots.elementResizeStart.next(); this._capPointerDown(e, 'target'); }); this._disposables.add(() => { @@ -76,11 +80,10 @@ export class EdgelessConnectorHandle extends WithDisposable(LitElement) { } private _capPointerDown(e: PointerEvent, connection: 'target' | 'source') { - const { edgeless, connector, _disposables } = this; - const { service } = edgeless; + const { gfx, connector, slots, _disposables } = this; e.stopPropagation(); _disposables.addFromEvent(document, 'pointermove', e => { - const point = service.viewport.toModelCoordFromClientCoord([e.x, e.y]); + const point = gfx.viewport.toModelCoordFromClientCoord([e.x, e.y]); const isStartPointer = connection === 'source'; const otherSideId = connector[isStartPointer ? 'target' : 'source'].id; @@ -96,16 +99,16 @@ export class EdgelessConnectorHandle extends WithDisposable(LitElement) { _disposables.dispose(); this._disposables = new DisposableGroup(); this._bindEvent(); - edgeless.slots.elementResizeEnd.next(); + slots.elementResizeEnd.next(); }); } override firstUpdated() { - const { edgeless } = this; - const { viewport } = edgeless.service; + const { gfx } = this; + const { viewport } = gfx; this._lastZoom = viewport.zoom; - edgeless.service.viewport.viewportUpdated.subscribe(() => { + viewport.viewportUpdated.subscribe(() => { if (viewport.zoom !== this._lastZoom) { this._lastZoom = viewport.zoom; this.requestUpdate(); @@ -116,10 +119,10 @@ export class EdgelessConnectorHandle extends WithDisposable(LitElement) { } override render() { - const { service } = this.edgeless; + const { gfx } = this; // path is relative to the element's xywh const { path } = this.connector; - const zoom = service.viewport.zoom; + const zoom = gfx.viewport.zoom; const startPoint = Vec.subScalar(Vec.mul(path[0], zoom), HALF_SIZE); const endPoint = Vec.subScalar( Vec.mul(path[path.length - 1], zoom), @@ -155,7 +158,7 @@ export class EdgelessConnectorHandle extends WithDisposable(LitElement) { accessor doc!: Store; @property({ attribute: false }) - accessor edgeless!: EdgelessRootBlockComponent; + accessor edgeless!: BlockComponent; @consume({ context: stdContext, diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/gfx-tool/connector-tool.ts b/blocksuite/affine/gfx/connector/src/connector-tool.ts similarity index 97% rename from blocksuite/affine/blocks/block-root/src/edgeless/gfx-tool/connector-tool.ts rename to blocksuite/affine/gfx/connector/src/connector-tool.ts index 9e1b5d2b92..c9a8c1b64d 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/gfx-tool/connector-tool.ts +++ b/blocksuite/affine/gfx/connector/src/connector-tool.ts @@ -101,6 +101,7 @@ export class ConnectorTool extends BaseTool { this._allowCancel = true; } + // @ts-expect-error FIXME: resolve after gfx tool refactor this.gfx.tool.setTool('default'); this.gfx.selection.set({ elements: [focusedId] }); } @@ -128,6 +129,7 @@ export class ConnectorTool extends BaseTool { const connector = this._connector; this.doc.captureSync(); + // @ts-expect-error FIXME: resolve after gfx tool refactor this.gfx.tool.setTool('default'); this.gfx.selection.set({ elements: [connector.id] }); } diff --git a/blocksuite/affine/gfx/connector/src/effects.ts b/blocksuite/affine/gfx/connector/src/effects.ts new file mode 100644 index 0000000000..08620298c8 --- /dev/null +++ b/blocksuite/affine/gfx/connector/src/effects.ts @@ -0,0 +1,26 @@ +import { EdgelessConnectorHandle } from './components/connector-handle'; +import { EdgelessConnectorLabelEditor } from './text/edgeless-connector-label-editor'; +import { EdgelessConnectorMenu } from './toolbar/connector-menu'; +import { EdgelessConnectorToolButton } from './toolbar/connector-tool-button'; + +export function effects() { + customElements.define( + 'edgeless-connector-tool-button', + EdgelessConnectorToolButton + ); + customElements.define('edgeless-connector-menu', EdgelessConnectorMenu); + customElements.define( + 'edgeless-connector-label-editor', + EdgelessConnectorLabelEditor + ); + customElements.define('edgeless-connector-handle', EdgelessConnectorHandle); +} + +declare global { + interface HTMLElementTagNameMap { + 'edgeless-connector-tool-button': EdgelessConnectorToolButton; + 'edgeless-connector-menu': EdgelessConnectorMenu; + 'edgeless-connector-label-editor': EdgelessConnectorLabelEditor; + 'edgeless-connector-handle': EdgelessConnectorHandle; + } +} diff --git a/blocksuite/affine/gfx/connector/src/index.ts b/blocksuite/affine/gfx/connector/src/index.ts new file mode 100644 index 0000000000..9462c92593 --- /dev/null +++ b/blocksuite/affine/gfx/connector/src/index.ts @@ -0,0 +1,4 @@ +export * from './connector-tool'; +export * from './text'; +export * from './toolbar/config'; +export * from './toolbar/quick-tool'; diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/text/edgeless-connector-label-editor.ts b/blocksuite/affine/gfx/connector/src/text/edgeless-connector-label-editor.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/text/edgeless-connector-label-editor.ts rename to blocksuite/affine/gfx/connector/src/text/edgeless-connector-label-editor.ts diff --git a/blocksuite/affine/gfx/connector/src/text/index.ts b/blocksuite/affine/gfx/connector/src/text/index.ts new file mode 100644 index 0000000000..9a89020a79 --- /dev/null +++ b/blocksuite/affine/gfx/connector/src/text/index.ts @@ -0,0 +1 @@ +export * from './text.js'; diff --git a/blocksuite/affine/gfx/connector/src/text/text.ts b/blocksuite/affine/gfx/connector/src/text/text.ts new file mode 100644 index 0000000000..9a8c3406af --- /dev/null +++ b/blocksuite/affine/gfx/connector/src/text/text.ts @@ -0,0 +1,65 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; +import type { ConnectorElementModel } from '@blocksuite/affine-model'; +import type { BlockComponent } from '@blocksuite/block-std'; +import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx'; +import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; +import type { IVec } from '@blocksuite/global/gfx'; +import { Bound } from '@blocksuite/global/gfx'; +import * as Y from 'yjs'; + +import { EdgelessConnectorLabelEditor } from './edgeless-connector-label-editor'; + +export function mountConnectorLabelEditor( + connector: ConnectorElementModel, + edgeless: BlockComponent, + point?: IVec +) { + const mountElm = edgeless.querySelector('.edgeless-mount-point'); + if (!mountElm) { + throw new BlockSuiteError( + ErrorCode.ValueNotExists, + "edgeless block's mount point does not exist" + ); + } + + const gfx = edgeless.std.get(GfxControllerIdentifier); + + // @ts-expect-error FIXME: resolve after gfx tool refactor + gfx.tool.setTool('default'); + gfx.selection.set({ + elements: [connector.id], + editing: true, + }); + + if (!connector.text) { + const text = new Y.Text(); + const labelOffset = connector.labelOffset; + let labelXYWH = connector.labelXYWH ?? [0, 0, 16, 16]; + + if (point) { + const center = connector.getNearestPoint(point); + const distance = connector.getOffsetDistanceByPoint(center as IVec); + const bounds = Bound.fromXYWH(labelXYWH); + bounds.center = center; + labelOffset.distance = distance; + labelXYWH = bounds.toXYWH(); + } + + edgeless.std.get(EdgelessCRUDIdentifier).updateElement(connector.id, { + text, + labelXYWH, + labelOffset: { ...labelOffset }, + }); + } + + const editor = new EdgelessConnectorLabelEditor(); + editor.connector = connector; + editor.edgeless = edgeless; + + mountElm.append(editor); + editor.updateComplete + .then(() => { + editor.inlineEditor?.focusEnd(); + }) + .catch(console.error); +} diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/connector.ts b/blocksuite/affine/gfx/connector/src/toolbar/config.ts similarity index 99% rename from blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/connector.ts rename to blocksuite/affine/gfx/connector/src/toolbar/config.ts index 2219fe7870..0c7c58b900 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/connector.ts +++ b/blocksuite/affine/gfx/connector/src/toolbar/config.ts @@ -57,7 +57,7 @@ import { import { html } from 'lit'; import { styleMap } from 'lit/directives/style-map.js'; -import { mountConnectorLabelEditor } from '../../utils/text'; +import { mountConnectorLabelEditor } from '../text'; const FRONT_ENDPOINT_STYLE_LIST = [ { @@ -123,7 +123,7 @@ const CONNECTOR_MODE_LIST = [ }, ] as const satisfies MenuItem[]; -export const builtinConnectorToolbarConfig = { +export const connectorToolbarConfig = { actions: [ { id: 'a.stroke-color', diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/connector/connector-dense-menu.ts b/blocksuite/affine/gfx/connector/src/toolbar/connector-dense-menu.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/connector/connector-dense-menu.ts rename to blocksuite/affine/gfx/connector/src/toolbar/connector-dense-menu.ts diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/connector/connector-menu.ts b/blocksuite/affine/gfx/connector/src/toolbar/connector-menu.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/connector/connector-menu.ts rename to blocksuite/affine/gfx/connector/src/toolbar/connector-menu.ts diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/connector/connector-tool-button.ts b/blocksuite/affine/gfx/connector/src/toolbar/connector-tool-button.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/connector/connector-tool-button.ts rename to blocksuite/affine/gfx/connector/src/toolbar/connector-tool-button.ts diff --git a/blocksuite/affine/gfx/connector/src/toolbar/quick-tool.ts b/blocksuite/affine/gfx/connector/src/toolbar/quick-tool.ts new file mode 100644 index 0000000000..0c0d4d8a33 --- /dev/null +++ b/blocksuite/affine/gfx/connector/src/toolbar/quick-tool.ts @@ -0,0 +1,14 @@ +import { QuickToolExtension } from '@blocksuite/affine-widget-edgeless-toolbar'; +import { html } from 'lit'; + +export const connectorQuickTool = QuickToolExtension( + 'connector', + ({ block }) => { + return { + type: 'connector', + content: html``, + }; + } +); diff --git a/blocksuite/affine/gfx/connector/tsconfig.json b/blocksuite/affine/gfx/connector/tsconfig.json new file mode 100644 index 0000000000..5b280fe89a --- /dev/null +++ b/blocksuite/affine/gfx/connector/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo" + }, + "include": ["./src"], + "references": [ + { "path": "../../blocks/block-surface" }, + { "path": "../../components" }, + { "path": "../../model" }, + { "path": "../../rich-text" }, + { "path": "../../shared" }, + { "path": "../../widgets/widget-edgeless-toolbar" }, + { "path": "../../../framework/block-std" }, + { "path": "../../../framework/global" }, + { "path": "../../../framework/store" } + ] +} diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/panel/icons.ts b/blocksuite/affine/gfx/note/src/components/icons.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/panel/icons.ts rename to blocksuite/affine/gfx/note/src/components/icons.ts diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/panel/note-display-mode-panel.ts b/blocksuite/affine/gfx/note/src/components/note-display-mode-panel.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/panel/note-display-mode-panel.ts rename to blocksuite/affine/gfx/note/src/components/note-display-mode-panel.ts diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/panel/note-shadow-panel.ts b/blocksuite/affine/gfx/note/src/components/note-shadow-panel.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/panel/note-shadow-panel.ts rename to blocksuite/affine/gfx/note/src/components/note-shadow-panel.ts diff --git a/blocksuite/affine/gfx/note/src/effects.ts b/blocksuite/affine/gfx/note/src/effects.ts index f96e3f3222..edd95bf081 100644 --- a/blocksuite/affine/gfx/note/src/effects.ts +++ b/blocksuite/affine/gfx/note/src/effects.ts @@ -1,3 +1,5 @@ +import { NoteDisplayModePanel } from './components/note-display-mode-panel'; +import { EdgelessNoteShadowPanel } from './components/note-shadow-panel'; import { EdgelessNoteMenu } from './toolbar/note-menu'; import { EdgelessNoteSeniorButton } from './toolbar/note-senior-button'; import { EdgelessNoteToolButton } from './toolbar/note-tool-button'; @@ -9,6 +11,8 @@ export function effects() { 'edgeless-note-senior-button', EdgelessNoteSeniorButton ); + customElements.define('edgeless-note-shadow-panel', EdgelessNoteShadowPanel); + customElements.define('note-display-mode-panel', NoteDisplayModePanel); } declare global { @@ -16,5 +20,7 @@ declare global { 'edgeless-note-tool-button': EdgelessNoteToolButton; 'edgeless-note-menu': EdgelessNoteMenu; 'edgeless-note-senior-button': EdgelessNoteSeniorButton; + 'edgeless-note-shadow-panel': EdgelessNoteShadowPanel; + 'note-display-mode-panel': NoteDisplayModePanel; } } diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/panel/shape-panel.ts b/blocksuite/affine/gfx/shape/src/components/shape-panel.ts similarity index 93% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/panel/shape-panel.ts rename to blocksuite/affine/gfx/shape/src/components/shape-panel.ts index dcd3438e31..e18ef8578a 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/components/panel/shape-panel.ts +++ b/blocksuite/affine/gfx/shape/src/components/shape-panel.ts @@ -1,13 +1,12 @@ -import { - ShapeComponentConfig, - type ShapeTool, -} from '@blocksuite/affine-gfx-shape'; import { ShapeStyle } from '@blocksuite/affine-model'; import { css, html, LitElement } from 'lit'; import { property } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; import { Subject } from 'rxjs'; +import type { ShapeTool } from '../shape-tool'; +import { ShapeComponentConfig } from '../toolbar/shape-menu-config'; + export class EdgelessShapePanel extends LitElement { static override styles = css` :host { diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/panel/shape-style-panel.ts b/blocksuite/affine/gfx/shape/src/components/shape-style-panel.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/panel/shape-style-panel.ts rename to blocksuite/affine/gfx/shape/src/components/shape-style-panel.ts diff --git a/blocksuite/affine/gfx/shape/src/effects.ts b/blocksuite/affine/gfx/shape/src/effects.ts index 0397f2051e..d38282b8a0 100644 --- a/blocksuite/affine/gfx/shape/src/effects.ts +++ b/blocksuite/affine/gfx/shape/src/effects.ts @@ -1,3 +1,5 @@ +import { EdgelessShapePanel } from './components/shape-panel'; +import { EdgelessShapeStylePanel } from './components/shape-style-panel'; import { EdgelessShapeMenu, EdgelessShapeToolButton, @@ -18,6 +20,8 @@ export function effects() { 'edgeless-toolbar-shape-draggable', EdgelessToolbarShapeDraggable ); + customElements.define('edgeless-shape-panel', EdgelessShapePanel); + customElements.define('edgeless-shape-style-panel', EdgelessShapeStylePanel); } declare global { @@ -27,5 +31,7 @@ declare global { 'edgeless-shape-tool-element': EdgelessShapeToolElement; 'edgeless-toolbar-shape-draggable': EdgelessToolbarShapeDraggable; 'edgeless-shape-tool-button': EdgelessShapeToolButton; + 'edgeless-shape-panel': EdgelessShapePanel; + 'edgeless-shape-style-panel': EdgelessShapeStylePanel; } } diff --git a/tests/blocksuite/e2e/utils/declare-test-window.ts b/tests/blocksuite/e2e/utils/declare-test-window.ts index 08f534cf06..07e0cb5307 100644 --- a/tests/blocksuite/e2e/utils/declare-test-window.ts +++ b/tests/blocksuite/e2e/utils/declare-test-window.ts @@ -1,9 +1,14 @@ import type { EditorHost } from '@blocksuite/affine/block-std'; import type * as Effects from '@blocksuite/affine/effects'; +import type * as ConnectorToolEffect from '@blocksuite/affine/gfx/connector'; +import type * as ShapeToolEffect from '@blocksuite/affine/gfx/shape'; import type { Store, Transformer, Workspace } from '@blocksuite/affine/store'; import type { TestAffineEditorContainer } from '@blocksuite/integration-test'; -declare const _GLOBAL_: typeof Effects; +declare const _GLOBAL_: + | typeof Effects + | typeof ConnectorToolEffect + | typeof ShapeToolEffect; declare global { interface Window { diff --git a/tools/utils/src/workspace.gen.ts b/tools/utils/src/workspace.gen.ts index 5d6838a793..f2aa524f45 100644 --- a/tools/utils/src/workspace.gen.ts +++ b/tools/utils/src/workspace.gen.ts @@ -28,6 +28,7 @@ export const PackageList = [ 'blocksuite/affine/fragments/fragment-doc-title', 'blocksuite/affine/fragments/fragment-frame-panel', 'blocksuite/affine/fragments/fragment-outline', + 'blocksuite/affine/gfx/connector', 'blocksuite/affine/gfx/note', 'blocksuite/affine/gfx/shape', 'blocksuite/affine/gfx/text', @@ -308,6 +309,7 @@ export const PackageList = [ 'blocksuite/affine/blocks/block-table', 'blocksuite/affine/components', 'blocksuite/affine/fragments/fragment-doc-title', + 'blocksuite/affine/gfx/connector', 'blocksuite/affine/gfx/note', 'blocksuite/affine/gfx/shape', 'blocksuite/affine/gfx/text', @@ -443,6 +445,21 @@ export const PackageList = [ 'blocksuite/framework/store', ], }, + { + location: 'blocksuite/affine/gfx/connector', + name: '@blocksuite/affine-gfx-connector', + workspaceDependencies: [ + 'blocksuite/affine/blocks/block-surface', + 'blocksuite/affine/components', + 'blocksuite/affine/model', + 'blocksuite/affine/rich-text', + 'blocksuite/affine/shared', + 'blocksuite/affine/widgets/widget-edgeless-toolbar', + 'blocksuite/framework/block-std', + 'blocksuite/framework/global', + 'blocksuite/framework/store', + ], + }, { location: 'blocksuite/affine/gfx/note', name: '@blocksuite/affine-gfx-note', @@ -1075,6 +1092,7 @@ export type PackageName = | '@blocksuite/affine-fragment-doc-title' | '@blocksuite/affine-fragment-frame-panel' | '@blocksuite/affine-fragment-outline' + | '@blocksuite/affine-gfx-connector' | '@blocksuite/affine-gfx-note' | '@blocksuite/affine-gfx-shape' | '@blocksuite/affine-gfx-text' diff --git a/tsconfig.json b/tsconfig.json index f876a17756..37d1092c64 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -75,6 +75,7 @@ { "path": "./blocksuite/affine/fragments/fragment-doc-title" }, { "path": "./blocksuite/affine/fragments/fragment-frame-panel" }, { "path": "./blocksuite/affine/fragments/fragment-outline" }, + { "path": "./blocksuite/affine/gfx/connector" }, { "path": "./blocksuite/affine/gfx/note" }, { "path": "./blocksuite/affine/gfx/shape" }, { "path": "./blocksuite/affine/gfx/text" }, diff --git a/yarn.lock b/yarn.lock index f81a2cacc5..934e84ee3d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2733,6 +2733,7 @@ __metadata: "@blocksuite/affine-block-table": "workspace:*" "@blocksuite/affine-components": "workspace:*" "@blocksuite/affine-fragment-doc-title": "workspace:*" + "@blocksuite/affine-gfx-connector": "workspace:*" "@blocksuite/affine-gfx-note": "workspace:*" "@blocksuite/affine-gfx-shape": "workspace:*" "@blocksuite/affine-gfx-text": "workspace:*" @@ -2969,6 +2970,33 @@ __metadata: languageName: unknown linkType: soft +"@blocksuite/affine-gfx-connector@workspace:*, @blocksuite/affine-gfx-connector@workspace:blocksuite/affine/gfx/connector": + version: 0.0.0-use.local + resolution: "@blocksuite/affine-gfx-connector@workspace:blocksuite/affine/gfx/connector" + dependencies: + "@blocksuite/affine-block-surface": "workspace:*" + "@blocksuite/affine-components": "workspace:*" + "@blocksuite/affine-model": "workspace:*" + "@blocksuite/affine-rich-text": "workspace:*" + "@blocksuite/affine-shared": "workspace:*" + "@blocksuite/affine-widget-edgeless-toolbar": "workspace:*" + "@blocksuite/block-std": "workspace:*" + "@blocksuite/global": "workspace:*" + "@blocksuite/icons": "npm:^2.2.6" + "@blocksuite/store": "workspace:*" + "@lit/context": "npm:^1.1.2" + "@preact/signals-core": "npm:^1.8.0" + "@toeverything/theme": "npm:^1.1.12" + "@types/lodash-es": "npm:^4.17.12" + lit: "npm:^3.2.0" + lodash-es: "npm:^4.17.21" + minimatch: "npm:^10.0.1" + rxjs: "npm:^7.8.1" + yjs: "npm:^13.6.21" + zod: "npm:^3.23.8" + languageName: unknown + linkType: soft + "@blocksuite/affine-gfx-note@workspace:*, @blocksuite/affine-gfx-note@workspace:blocksuite/affine/gfx/note": version: 0.0.0-use.local resolution: "@blocksuite/affine-gfx-note@workspace:blocksuite/affine/gfx/note" @@ -3511,6 +3539,7 @@ __metadata: "@blocksuite/affine-fragment-doc-title": "workspace:*" "@blocksuite/affine-fragment-frame-panel": "workspace:*" "@blocksuite/affine-fragment-outline": "workspace:*" + "@blocksuite/affine-gfx-connector": "workspace:*" "@blocksuite/affine-gfx-note": "workspace:*" "@blocksuite/affine-gfx-shape": "workspace:*" "@blocksuite/affine-gfx-text": "workspace:*"