refactor: rewrite dblclick and selection logic of default-tool (#11036)

continue #10824

### Changed
- Moved double-click-to-edit behavior from the default tool to individual model views
- Introduced `onSelected` callback interface in gfx view components to allows developers to override default selection logic
This commit is contained in:
doouding
2025-03-26 07:32:43 +00:00
parent 61c0d01da3
commit ace5d44a61
38 changed files with 779 additions and 353 deletions

View File

@@ -1,14 +1,18 @@
import {
CanvasElementType,
EdgelessCRUDIdentifier,
getSurfaceBlock,
type IModelCoord,
TextUtils,
} from '@blocksuite/affine-block-surface';
import type { TextElementModel } from '@blocksuite/affine-model';
import { TextElementModel } from '@blocksuite/affine-model';
import type { RichText } from '@blocksuite/affine-rich-text';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { getSelectedRect } from '@blocksuite/affine-shared/utils';
import {
type BlockComponent,
type BlockStdScope,
type PointerEventState,
ShadowlessElement,
stdContext,
} from '@blocksuite/block-std';
@@ -20,6 +24,71 @@ import { consume } from '@lit/context';
import { css, html, nothing } from 'lit';
import { property, query } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
import * as Y from 'yjs';
export function mountTextElementEditor(
textElement: TextElementModel,
edgeless: BlockComponent,
focusCoord?: IModelCoord
) {
let cursorIndex = textElement.text.length;
if (focusCoord) {
cursorIndex = Math.min(
TextUtils.getCursorByCoord(textElement, focusCoord),
cursorIndex
);
}
const textEditor = new EdgelessTextEditor();
textEditor.element = textElement;
edgeless.append(textEditor);
textEditor.updateComplete
.then(() => {
textEditor.inlineEditor?.focusIndex(cursorIndex);
})
.catch(console.error);
const gfx = edgeless.std.get(GfxControllerIdentifier);
// @ts-expect-error TODO: refactor gfx tool
gfx.tool.setTool('default');
gfx.selection.set({
elements: [textElement.id],
editing: true,
});
}
/**
* @deprecated
*
* Canvas Text has been deprecated
*/
export function addText(edgeless: BlockComponent, event: PointerEventState) {
const gfx = edgeless.std.get(GfxControllerIdentifier);
const crud = edgeless.std.get(EdgelessCRUDIdentifier);
const [x, y] = gfx.viewport.toModelCoord(event.x, event.y);
const selected = gfx.getElementByPoint(x, y);
if (!selected) {
const [modelX, modelY] = gfx.viewport.toModelCoord(event.x, event.y);
const id = edgeless.std
.get(EdgelessCRUDIdentifier)
.addElement(CanvasElementType.TEXT, {
xywh: new Bound(modelX, modelY, 32, 32).serialize(),
text: new Y.Text(),
});
if (!id) return;
edgeless.doc.captureSync();
const textElement = crud.getElementById(id);
if (!textElement) return;
if (textElement instanceof TextElementModel) {
mountTextElementEditor(textElement, edgeless);
}
}
}
export class EdgelessTextEditor extends WithDisposable(ShadowlessElement) {
get crud() {

View File

@@ -1,4 +1,4 @@
export * from './edgeless-text-editor';
export * from './mount-text-editor';
export * from './tool';
export * from './toolbar';
export * from './view';

View File

@@ -0,0 +1,28 @@
import type { TextElementModel } from '@blocksuite/affine-model';
import { GfxElementModelView } from '@blocksuite/block-std/gfx';
import { mountTextElementEditor } from './edgeless-text-editor';
export class TextElementView extends GfxElementModelView<TextElementModel> {
static override type: string = 'text';
override onCreated(): void {
super.onCreated();
this._initDblClickToEdit();
}
private _initDblClickToEdit(): void {
this.on('dblclick', evt => {
const edgeless = this.std.view.getBlock(this.std.store.root!.id);
const [x, y] = this.gfx.viewport.toModelCoord(evt.x, evt.y);
if (edgeless && !this.model.isLocked()) {
mountTextElementEditor(this.model, edgeless, {
x,
y,
});
}
});
}
}