refactor(editor): improve edgeless editors (#11019)

This commit is contained in:
Saul-Mirone
2025-03-20 01:49:56 +00:00
parent 258c70cf07
commit cdd405bbe5
7 changed files with 138 additions and 89 deletions

View File

@@ -1,5 +1,6 @@
import { import {
EdgelessCRUDIdentifier, EdgelessCRUDIdentifier,
getSurfaceBlock,
TextUtils, TextUtils,
} from '@blocksuite/affine-block-surface'; } from '@blocksuite/affine-block-surface';
import type { ConnectorElementModel } from '@blocksuite/affine-model'; import type { ConnectorElementModel } from '@blocksuite/affine-model';
@@ -7,9 +8,11 @@ import type { RichText } from '@blocksuite/affine-rich-text';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { almostEqual } from '@blocksuite/affine-shared/utils'; import { almostEqual } from '@blocksuite/affine-shared/utils';
import { import {
type BlockComponent,
RANGE_SYNC_EXCLUDE_ATTR, RANGE_SYNC_EXCLUDE_ATTR,
ShadowlessElement, ShadowlessElement,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { Bound, Vec } from '@blocksuite/global/gfx'; import { Bound, Vec } from '@blocksuite/global/gfx';
import { WithDisposable } from '@blocksuite/global/lit'; import { WithDisposable } from '@blocksuite/global/lit';
import { css, html, nothing } from 'lit'; import { css, html, nothing } from 'lit';
@@ -17,8 +20,6 @@ import { property, query } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import * as Y from 'yjs'; import * as Y from 'yjs';
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
const HORIZONTAL_PADDING = 2; const HORIZONTAL_PADDING = 2;
const VERTICAL_PADDING = 2; const VERTICAL_PADDING = 2;
const BORDER_WIDTH = 1; const BORDER_WIDTH = 1;
@@ -63,6 +64,14 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
return this.edgeless.std.get(EdgelessCRUDIdentifier); return this.edgeless.std.get(EdgelessCRUDIdentifier);
} }
get gfx() {
return this.edgeless.std.get(GfxControllerIdentifier);
}
get selection() {
return this.gfx.selection;
}
private _isComposition = false; private _isComposition = false;
private _keeping = false; private _keeping = false;
@@ -113,8 +122,9 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
} }
override firstUpdated() { override firstUpdated() {
const { edgeless, connector } = this; const { edgeless, connector, selection } = this;
const { dispatcher } = edgeless; const dispatcher = edgeless.std.event;
const store = edgeless.std.store;
this._resizeObserver = new ResizeObserver(() => { this._resizeObserver = new ResizeObserver(() => {
this._updateLabelRect(); this._updateLabelRect();
@@ -142,7 +152,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
if (!isComposing && (isModEnter || isEscape)) { if (!isComposing && (isModEnter || isEscape)) {
this.inlineEditorContainer?.blur(); this.inlineEditorContainer?.blur();
edgeless.service.selection.set({ selection.set({
elements: [connector.id], elements: [connector.id],
editing: false, editing: false,
}); });
@@ -152,14 +162,18 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
}) })
); );
this.disposables.add( const surface = getSurfaceBlock(store);
edgeless.service.surface.elementUpdated.subscribe(({ id }) => {
if (id === connector.id) this.requestUpdate(); if (surface) {
}) this.disposables.add(
); surface.elementUpdated.subscribe(({ id }) => {
if (id === connector.id) this.requestUpdate();
})
);
}
this.disposables.add( this.disposables.add(
edgeless.service.viewport.viewportUpdated.subscribe(() => { this.gfx.viewport.viewportUpdated.subscribe(() => {
this.requestUpdate(); this.requestUpdate();
}) })
); );
@@ -189,7 +203,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
connector.lableEditing = false; connector.lableEditing = false;
edgeless.service.selection.set({ selection.set({
elements: [], elements: [],
editing: false, editing: false,
}); });
@@ -254,7 +268,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
fontSize, fontSize,
fontWeight fontWeight
); );
const { translateX, translateY, zoom } = this.edgeless.service.viewport; const { translateX, translateY, zoom } = this.gfx.viewport;
const [x, y] = Vec.mul(connector.getPointByOffsetDistance(distance), zoom); const [x, y] = Vec.mul(connector.getPointByOffsetDistance(distance), zoom);
const transformOperation = [ const transformOperation = [
'translate(-50%, -50%)', 'translate(-50%, -50%)',
@@ -316,7 +330,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
accessor connector!: ConnectorElementModel; accessor connector!: ConnectorElementModel;
@property({ attribute: false }) @property({ attribute: false })
accessor edgeless!: EdgelessRootBlockComponent; accessor edgeless!: BlockComponent;
@query('rich-text') @query('rich-text')
accessor richText!: RichText; accessor richText!: RichText;

View File

@@ -6,9 +6,11 @@ import {
frameTitleStyleVars, frameTitleStyleVars,
} from '@blocksuite/affine-widget-frame-title'; } from '@blocksuite/affine-widget-frame-title';
import { import {
type BlockComponent,
RANGE_SYNC_EXCLUDE_ATTR, RANGE_SYNC_EXCLUDE_ATTR,
ShadowlessElement, ShadowlessElement,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { Bound } from '@blocksuite/global/gfx'; import { Bound } from '@blocksuite/global/gfx';
import { WithDisposable } from '@blocksuite/global/lit'; import { WithDisposable } from '@blocksuite/global/lit';
import { cssVarV2 } from '@toeverything/theme/v2'; import { cssVarV2 } from '@toeverything/theme/v2';
@@ -16,8 +18,6 @@ import { css, html, nothing } from 'lit';
import { property, query } from 'lit/decorators.js'; import { property, query } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
export class EdgelessFrameTitleEditor extends WithDisposable( export class EdgelessFrameTitleEditor extends WithDisposable(
ShadowlessElement ShadowlessElement
) { ) {
@@ -46,10 +46,18 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
return this.richText?.inlineEditor; return this.richText?.inlineEditor;
} }
get gfx() {
return this.edgeless.std.get(GfxControllerIdentifier);
}
get selection() {
return this.gfx.selection;
}
private _unmount() { private _unmount() {
// dispose in advance to avoid execute `this.remove()` twice // dispose in advance to avoid execute `this.remove()` twice
this.disposables.dispose(); this.disposables.dispose();
this.edgeless.service.selection.set({ this.selection.set({
elements: [], elements: [],
editing: false, editing: false,
}); });
@@ -62,7 +70,7 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
} }
override firstUpdated(): void { override firstUpdated(): void {
const dispatcher = this.edgeless.dispatcher; const dispatcher = this.edgeless.std.event;
this.updateComplete this.updateComplete
.then(() => { .then(() => {
if (!this.inlineEditor) return; if (!this.inlineEditor) return;
@@ -87,7 +95,7 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
}) })
); );
this.disposables.add( this.disposables.add(
this.edgeless.service.viewport.viewportUpdated.subscribe(() => { this.gfx.viewport.viewportUpdated.subscribe(() => {
this.requestUpdate(); this.requestUpdate();
}) })
); );
@@ -117,10 +125,10 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
const rootBlockId = this.editorHost.doc.root?.id; const rootBlockId = this.editorHost.doc.root?.id;
if (!rootBlockId) return nothing; if (!rootBlockId) return nothing;
const viewport = this.edgeless.service.viewport; const viewport = this.gfx.viewport;
const bound = Bound.deserialize(this.frameModel.xywh); const bound = Bound.deserialize(this.frameModel.xywh);
const [x, y] = viewport.toViewCoord(bound.x, bound.y); const [x, y] = viewport.toViewCoord(bound.x, bound.y);
const isInner = this.edgeless.service.gfx.grid.has( const isInner = this.gfx.grid.has(
this.frameModel.elementBound, this.frameModel.elementBound,
true, true,
true, true,
@@ -167,7 +175,7 @@ export class EdgelessFrameTitleEditor extends WithDisposable(
} }
@property({ attribute: false }) @property({ attribute: false })
accessor edgeless!: EdgelessRootBlockComponent; accessor edgeless!: BlockComponent;
@property({ attribute: false }) @property({ attribute: false })
accessor frameModel!: FrameBlockModel; accessor frameModel!: FrameBlockModel;

View File

@@ -6,17 +6,17 @@ import {
import type { GroupElementModel } from '@blocksuite/affine-model'; import type { GroupElementModel } from '@blocksuite/affine-model';
import type { RichText } from '@blocksuite/affine-rich-text'; import type { RichText } from '@blocksuite/affine-rich-text';
import { import {
type BlockComponent,
RANGE_SYNC_EXCLUDE_ATTR, RANGE_SYNC_EXCLUDE_ATTR,
ShadowlessElement, ShadowlessElement,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { Bound } from '@blocksuite/global/gfx'; import { Bound } from '@blocksuite/global/gfx';
import { WithDisposable } from '@blocksuite/global/lit'; import { WithDisposable } from '@blocksuite/global/lit';
import { html, nothing } from 'lit'; import { html, nothing } from 'lit';
import { property, query } from 'lit/decorators.js'; import { property, query } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
export class EdgelessGroupTitleEditor extends WithDisposable( export class EdgelessGroupTitleEditor extends WithDisposable(
ShadowlessElement ShadowlessElement
) { ) {
@@ -28,11 +28,19 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
return this.inlineEditor?.rootElement; return this.inlineEditor?.rootElement;
} }
get gfx() {
return this.edgeless.std.get(GfxControllerIdentifier);
}
get selection() {
return this.gfx.selection;
}
private _unmount() { private _unmount() {
// dispose in advance to avoid execute `this.remove()` twice // dispose in advance to avoid execute `this.remove()` twice
this.disposables.dispose(); this.disposables.dispose();
this.group.showTitle = true; this.group.showTitle = true;
this.edgeless.service.selection.set({ this.selection.set({
elements: [this.group.id], elements: [this.group.id],
editing: false, editing: false,
}); });
@@ -45,7 +53,7 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
} }
override firstUpdated(): void { override firstUpdated(): void {
const dispatcher = this.edgeless.dispatcher; const dispatcher = this.edgeless.std.event;
this.updateComplete this.updateComplete
.then(() => { .then(() => {
@@ -72,7 +80,7 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
}) })
); );
this.disposables.add( this.disposables.add(
this.edgeless.service.viewport.viewportUpdated.subscribe(() => { this.gfx.viewport.viewportUpdated.subscribe(() => {
this.requestUpdate(); this.requestUpdate();
}) })
); );
@@ -103,7 +111,7 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
console.error('group.externalXYWH is not set'); console.error('group.externalXYWH is not set');
return nothing; return nothing;
} }
const viewport = this.edgeless.service.viewport; const viewport = this.gfx.viewport;
const bound = Bound.deserialize(this.group.externalXYWH); const bound = Bound.deserialize(this.group.externalXYWH);
const [x, y] = viewport.toViewCoord(bound.x, bound.y); const [x, y] = viewport.toViewCoord(bound.x, bound.y);
@@ -137,7 +145,7 @@ export class EdgelessGroupTitleEditor extends WithDisposable(
} }
@property({ attribute: false }) @property({ attribute: false })
accessor edgeless!: EdgelessRootBlockComponent; accessor edgeless!: BlockComponent;
@property({ attribute: false }) @property({ attribute: false })
accessor group!: GroupElementModel; accessor group!: GroupElementModel;

View File

@@ -8,9 +8,11 @@ import type { RichText } from '@blocksuite/affine-rich-text';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { getSelectedRect } from '@blocksuite/affine-shared/utils'; import { getSelectedRect } from '@blocksuite/affine-shared/utils';
import { import {
type BlockComponent,
RANGE_SYNC_EXCLUDE_ATTR, RANGE_SYNC_EXCLUDE_ATTR,
ShadowlessElement, ShadowlessElement,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { Bound, toRadian, Vec } from '@blocksuite/global/gfx'; import { Bound, toRadian, Vec } from '@blocksuite/global/gfx';
import { WithDisposable } from '@blocksuite/global/lit'; import { WithDisposable } from '@blocksuite/global/lit';
import { html, nothing } from 'lit'; import { html, nothing } from 'lit';
@@ -18,13 +20,7 @@ import { property, query } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import * as Y from 'yjs'; import * as Y from 'yjs';
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) { export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
private _keeping = false; private _keeping = false;
private _lastXYWH = ''; private _lastXYWH = '';
@@ -35,6 +31,18 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
return this.richText.inlineEditor; return this.richText.inlineEditor;
} }
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
get gfx() {
return this.edgeless.std.get(GfxControllerIdentifier);
}
get selection() {
return this.gfx.selection;
}
get inlineEditorContainer() { get inlineEditorContainer() {
return this.inlineEditor?.rootElement; return this.inlineEditor?.rootElement;
} }
@@ -47,8 +55,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
if (!this.isMindMapNode) { if (!this.isMindMapNode) {
return; return;
} }
const selection = this.selection;
const service = this.edgeless.service;
this._disposables.addFromEvent(this, 'keydown', evt => { this._disposables.addFromEvent(this, 'keydown', evt => {
switch (evt.key) { switch (evt.key) {
@@ -57,7 +64,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
if (evt.shiftKey || evt.isComposing) return; if (evt.shiftKey || evt.isComposing) return;
(this.ownerDocument.activeElement as HTMLElement).blur(); (this.ownerDocument.activeElement as HTMLElement).blur();
service.selection.set({ selection.set({
elements: [this.element.id], elements: [this.element.id],
editing: false, editing: false,
}); });
@@ -67,7 +74,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
case 'Tab': { case 'Tab': {
evt.stopPropagation(); evt.stopPropagation();
(this.ownerDocument.activeElement as HTMLElement).blur(); (this.ownerDocument.activeElement as HTMLElement).blur();
service.selection.set({ selection.set({
elements: [this.element.id], elements: [this.element.id],
editing: false, editing: false,
}); });
@@ -109,7 +116,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
this.element.textDisplay = true; this.element.textDisplay = true;
this.remove(); this.remove();
this.edgeless.service.selection.set({ this.selection.set({
elements: [], elements: [],
editing: false, editing: false,
}); });
@@ -134,8 +141,10 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
toRadian(-this.element.rotate) toRadian(-this.element.rotate)
); );
const [modelLeftTopX, modelLeftTopY] = const [modelLeftTopX, modelLeftTopY] = this.gfx.viewport.toModelCoord(
this.edgeless.service.viewport.toModelCoord(leftTopX, leftTopY); leftTopX,
leftTopY
);
this.crud.updateElement(this.element.id, { this.crud.updateElement(this.element.id, {
xywh: new Bound( xywh: new Bound(
@@ -161,7 +170,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
this.richText.style.minHeight = `${containerHeight}px`; this.richText.style.minHeight = `${containerHeight}px`;
} }
this.edgeless.service.selection.set({ this.selection.set({
elements: [this.element.id], elements: [this.element.id],
editing: true, editing: true,
}); });
@@ -173,12 +182,12 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
} }
override firstUpdated(): void { override firstUpdated(): void {
const dispatcher = this.edgeless.dispatcher; const dispatcher = this.edgeless.std.event;
this.element.textDisplay = false; this.element.textDisplay = false;
this.disposables.add( this.disposables.add(
this.edgeless.service.viewport.viewportUpdated.subscribe(() => { this.gfx.viewport.viewportUpdated.subscribe(() => {
this.requestUpdate(); this.requestUpdate();
this.updateComplete this.updateComplete
.then(() => { .then(() => {
@@ -228,7 +237,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
this.disposables.addFromEvent(this, 'keydown', evt => { this.disposables.addFromEvent(this, 'keydown', evt => {
if (evt.key === 'Escape') { if (evt.key === 'Escape') {
requestAnimationFrame(() => { requestAnimationFrame(() => {
this.edgeless.service.selection.set({ this.selection.set({
elements: [this.element.id], elements: [this.element.id],
editing: false, editing: false,
}); });
@@ -256,7 +265,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
const [verticalPadding, horiPadding] = this.element.padding; const [verticalPadding, horiPadding] = this.element.padding;
const textResizing = this.element.textResizing; const textResizing = this.element.textResizing;
const viewport = this.edgeless.service.viewport; const viewport = this.gfx.viewport;
const zoom = viewport.zoom; const zoom = viewport.zoom;
const rect = getSelectedRect([this.element]); const rect = getSelectedRect([this.element]);
const rotate = this.element.rotate; const rotate = this.element.rotate;
@@ -265,10 +274,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
[rect.left + rect.width / 2, rect.top + rect.height / 2], [rect.left + rect.width / 2, rect.top + rect.height / 2],
toRadian(rotate) toRadian(rotate)
); );
const [x, y] = this.edgeless.service.viewport.toViewCoord( const [x, y] = this.gfx.viewport.toViewCoord(leftTopX, leftTopY);
leftTopX,
leftTopY
);
const autoWidth = textResizing === TextResizing.AUTO_WIDTH_AND_HEIGHT; const autoWidth = textResizing === TextResizing.AUTO_WIDTH_AND_HEIGHT;
const color = this.edgeless.std const color = this.edgeless.std
.get(ThemeProvider) .get(ThemeProvider)
@@ -344,17 +350,14 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) {
} }
@property({ attribute: false }) @property({ attribute: false })
accessor edgeless!: EdgelessRootBlockComponent; accessor edgeless!: BlockComponent;
@property({ attribute: false }) @property({ attribute: false })
accessor element!: ShapeElementModel; accessor element!: ShapeElementModel;
@property({ attribute: false }) @property({ attribute: false })
accessor mountEditor: accessor mountEditor:
| (( | ((element: ShapeElementModel, edgeless: BlockComponent) => void)
element: ShapeElementModel,
edgeless: EdgelessRootBlockComponent
) => void)
| undefined = undefined; | undefined = undefined;
@query('rich-text') @query('rich-text')

View File

@@ -29,7 +29,7 @@ import {
handleNativeRangeAtPoint, handleNativeRangeAtPoint,
resetNativeSelection, resetNativeSelection,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import type { PointerEventState } from '@blocksuite/block-std'; import type { BlockComponent, PointerEventState } from '@blocksuite/block-std';
import { import {
BaseTool, BaseTool,
getTopElements, getTopElements,
@@ -41,12 +41,11 @@ import {
import { DisposableGroup } from '@blocksuite/global/disposable'; import { DisposableGroup } from '@blocksuite/global/disposable';
import type { IVec } from '@blocksuite/global/gfx'; import type { IVec } from '@blocksuite/global/gfx';
import { Bound, getCommonBoundWithRotation, Vec } from '@blocksuite/global/gfx'; import { Bound, getCommonBoundWithRotation, Vec } from '@blocksuite/global/gfx';
import { noop } from '@blocksuite/global/utils';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
import clamp from 'lodash-es/clamp'; import clamp from 'lodash-es/clamp';
import last from 'lodash-es/last'; import last from 'lodash-es/last';
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js'; import type { EdgelessRootBlockComponent } from '../index.js';
import { prepareCloneData } from '../utils/clone-utils.js'; import { prepareCloneData } from '../utils/clone-utils.js';
import { calPanDelta } from '../utils/panning-utils.js'; import { calPanDelta } from '../utils/panning-utils.js';
import { isCanvasElement, isEdgelessTextBlock } from '../utils/query.js'; import { isCanvasElement, isEdgelessTextBlock } from '../utils/query.js';
@@ -191,10 +190,8 @@ export class DefaultTool extends BaseTool {
enableHover = true; enableHover = true;
private get _edgeless(): EdgelessRootBlockComponent | null { private get _edgeless(): BlockComponent | null {
const block = this.std.view.getBlock(this.doc.root!.id); return this.std.view.getBlock(this.doc.root!.id);
return (block as EdgelessRootBlockComponent) ?? null;
} }
/** /**
@@ -243,7 +240,11 @@ export class DefaultTool extends BaseTool {
private async _cloneContent() { private async _cloneContent() {
if (!this._edgeless) return; if (!this._edgeless) return;
const clipboardController = this._edgeless?.clipboardController; // FIXME: edgeless clipboard should be an extension
const clipboardController = (
this._edgeless as EdgelessRootBlockComponent | null
)?.clipboardController;
if (!clipboardController) return;
const snapshot = prepareCloneData(this._toBeMoved, this.std); const snapshot = prepareCloneData(this._toBeMoved, this.std);
const bound = getCommonBoundWithRotation(this._toBeMoved); const bound = getCommonBoundWithRotation(this._toBeMoved);
@@ -551,7 +552,6 @@ export class DefaultTool extends BaseTool {
this._stopAutoPanning(); this._stopAutoPanning();
this._clearDisposable(); this._clearDisposable();
this._accumulateDelta = [0, 0]; this._accumulateDelta = [0, 0];
noop();
} }
override doubleClick(e: PointerEventState) { override doubleClick(e: PointerEventState) {

View File

@@ -1,5 +1,6 @@
import { import {
EdgelessFrameManager, EdgelessFrameManager,
EdgelessFrameManagerIdentifier,
isFrameBlock, isFrameBlock,
} from '@blocksuite/affine-block-frame'; } from '@blocksuite/affine-block-frame';
import { isNoteBlock } from '@blocksuite/affine-block-surface'; import { isNoteBlock } from '@blocksuite/affine-block-surface';
@@ -13,6 +14,7 @@ import type {
ShapeElementModel, ShapeElementModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { getElementsWithoutGroup } from '@blocksuite/affine-shared/utils'; import { getElementsWithoutGroup } from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std';
import { import {
generateKeyBetweenV2, generateKeyBetweenV2,
type GfxModel, type GfxModel,
@@ -99,7 +101,7 @@ type FrameSnapshot = BlockSnapshot & {
export function createNewPresentationIndexes( export function createNewPresentationIndexes(
raw: (SerializedElement | BlockSnapshot)[], raw: (SerializedElement | BlockSnapshot)[],
edgeless: EdgelessRootBlockComponent edgeless: BlockComponent
) { ) {
const frames = raw const frames = raw
.filter((block): block is FrameSnapshot => { .filter((block): block is FrameSnapshot => {
@@ -108,7 +110,7 @@ export function createNewPresentationIndexes(
}) })
.sort((a, b) => EdgelessFrameManager.framePresentationComparator(a, b)); .sort((a, b) => EdgelessFrameManager.framePresentationComparator(a, b));
const frameMgr = edgeless.service.frame; const frameMgr = edgeless.std.get(EdgelessFrameManagerIdentifier);
let before = frameMgr.generatePresentationIndex(); let before = frameMgr.generatePresentationIndex();
const result = new Map<string, string>(); const result = new Map<string, string>();
frames.forEach(frame => { frames.forEach(frame => {

View File

@@ -5,6 +5,8 @@ import type {
GroupElementModel, GroupElementModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { ShapeElementModel } from '@blocksuite/affine-model'; import { ShapeElementModel } 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 { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import type { IVec } from '@blocksuite/global/gfx'; import type { IVec } from '@blocksuite/global/gfx';
import { Bound } from '@blocksuite/global/gfx'; import { Bound } from '@blocksuite/global/gfx';
@@ -14,28 +16,31 @@ import { EdgelessConnectorLabelEditor } from '../components/text/edgeless-connec
import { EdgelessFrameTitleEditor } from '../components/text/edgeless-frame-title-editor.js'; import { EdgelessFrameTitleEditor } from '../components/text/edgeless-frame-title-editor.js';
import { EdgelessGroupTitleEditor } from '../components/text/edgeless-group-title-editor.js'; import { EdgelessGroupTitleEditor } from '../components/text/edgeless-group-title-editor.js';
import { EdgelessShapeTextEditor } from '../components/text/edgeless-shape-text-editor.js'; import { EdgelessShapeTextEditor } from '../components/text/edgeless-shape-text-editor.js';
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js';
export function mountShapeTextEditor( export function mountShapeTextEditor(
shapeElement: ShapeElementModel, shapeElement: ShapeElementModel,
edgeless: EdgelessRootBlockComponent edgeless: BlockComponent
) { ) {
if (!edgeless.mountElm) { const mountElm = edgeless.querySelector('.edgeless-mount-point');
if (!mountElm) {
throw new BlockSuiteError( throw new BlockSuiteError(
ErrorCode.ValueNotExists, ErrorCode.ValueNotExists,
"edgeless block's mount point does not exist" "edgeless block's mount point does not exist"
); );
} }
const updatedElement = edgeless.service.crud.getElementById(shapeElement.id); const gfx = edgeless.std.get(GfxControllerIdentifier);
const crud = edgeless.std.get(EdgelessCRUDIdentifier);
const updatedElement = crud.getElementById(shapeElement.id);
if (!(updatedElement instanceof ShapeElementModel)) { if (!(updatedElement instanceof ShapeElementModel)) {
console.error('Cannot mount text editor on a non-shape element'); console.error('Cannot mount text editor on a non-shape element');
return; return;
} }
edgeless.gfx.tool.setTool('default'); gfx.tool.setTool('default');
edgeless.gfx.selection.set({ gfx.selection.set({
elements: [shapeElement.id], elements: [shapeElement.id],
editing: true, editing: true,
}); });
@@ -52,22 +57,25 @@ export function mountShapeTextEditor(
shapeEditor.edgeless = edgeless; shapeEditor.edgeless = edgeless;
shapeEditor.mountEditor = mountShapeTextEditor; shapeEditor.mountEditor = mountShapeTextEditor;
edgeless.mountElm.append(shapeEditor); mountElm.append(shapeEditor);
} }
export function mountFrameTitleEditor( export function mountFrameTitleEditor(
frame: FrameBlockModel, frame: FrameBlockModel,
edgeless: EdgelessRootBlockComponent edgeless: BlockComponent
) { ) {
if (!edgeless.mountElm) { const mountElm = edgeless.querySelector('.edgeless-mount-point');
if (!mountElm) {
throw new BlockSuiteError( throw new BlockSuiteError(
ErrorCode.ValueNotExists, ErrorCode.ValueNotExists,
"edgeless block's mount point does not exist" "edgeless block's mount point does not exist"
); );
} }
edgeless.gfx.tool.setTool('default'); const gfx = edgeless.std.get(GfxControllerIdentifier);
edgeless.gfx.selection.set({
gfx.tool.setTool('default');
gfx.selection.set({
elements: [frame.id], elements: [frame.id],
editing: true, editing: true,
}); });
@@ -76,22 +84,25 @@ export function mountFrameTitleEditor(
frameEditor.frameModel = frame; frameEditor.frameModel = frame;
frameEditor.edgeless = edgeless; frameEditor.edgeless = edgeless;
edgeless.mountElm.append(frameEditor); mountElm.append(frameEditor);
} }
export function mountGroupTitleEditor( export function mountGroupTitleEditor(
group: GroupElementModel, group: GroupElementModel,
edgeless: EdgelessRootBlockComponent edgeless: BlockComponent
) { ) {
if (!edgeless.mountElm) { const mountElm = edgeless.querySelector('.edgeless-mount-point');
if (!mountElm) {
throw new BlockSuiteError( throw new BlockSuiteError(
ErrorCode.ValueNotExists, ErrorCode.ValueNotExists,
"edgeless block's mount point does not exist" "edgeless block's mount point does not exist"
); );
} }
edgeless.gfx.tool.setTool('default'); const gfx = edgeless.std.get(GfxControllerIdentifier);
edgeless.gfx.selection.set({
gfx.tool.setTool('default');
gfx.selection.set({
elements: [group.id], elements: [group.id],
editing: true, editing: true,
}); });
@@ -100,23 +111,26 @@ export function mountGroupTitleEditor(
groupEditor.group = group; groupEditor.group = group;
groupEditor.edgeless = edgeless; groupEditor.edgeless = edgeless;
edgeless.mountElm.append(groupEditor); mountElm.append(groupEditor);
} }
export function mountConnectorLabelEditor( export function mountConnectorLabelEditor(
connector: ConnectorElementModel, connector: ConnectorElementModel,
edgeless: EdgelessRootBlockComponent, edgeless: BlockComponent,
point?: IVec point?: IVec
) { ) {
if (!edgeless.mountElm) { const mountElm = edgeless.querySelector('.edgeless-mount-point');
if (!mountElm) {
throw new BlockSuiteError( throw new BlockSuiteError(
ErrorCode.ValueNotExists, ErrorCode.ValueNotExists,
"edgeless block's mount point does not exist" "edgeless block's mount point does not exist"
); );
} }
edgeless.gfx.tool.setTool('default'); const gfx = edgeless.std.get(GfxControllerIdentifier);
edgeless.gfx.selection.set({
gfx.tool.setTool('default');
gfx.selection.set({
elements: [connector.id], elements: [connector.id],
editing: true, editing: true,
}); });
@@ -146,7 +160,7 @@ export function mountConnectorLabelEditor(
editor.connector = connector; editor.connector = connector;
editor.edgeless = edgeless; editor.edgeless = edgeless;
edgeless.mountElm.append(editor); mountElm.append(editor);
editor.updateComplete editor.updateComplete
.then(() => { .then(() => {
editor.inlineEditor?.focusEnd(); editor.inlineEditor?.focusEnd();