refactor(editor): remove gfx tool global type (#12116)

Closes: BS-2650
This commit is contained in:
Saul-Mirone
2025-05-04 13:53:26 +00:00
parent f3b5c36cf7
commit 30a2e5b4fb
95 changed files with 664 additions and 521 deletions

View File

@@ -1,18 +1,35 @@
import type { SurfaceBlockComponent } from '@blocksuite/affine-block-surface';
import {
addNote,
DEFAULT_NOTE_OFFSET_X,
DEFAULT_NOTE_OFFSET_Y,
DefaultTool,
EdgelessCRUDIdentifier,
EXCLUDING_MOUSE_OUT_CLASS_LIST,
type SurfaceBlockComponent,
} from '@blocksuite/affine-block-surface';
import {
DEFAULT_NOTE_HEIGHT,
DEFAULT_NOTE_WIDTH,
NOTE_MIN_HEIGHT,
type NoteBlockModel,
NoteDisplayMode,
} from '@blocksuite/affine-model';
import { EditPropsStore } from '@blocksuite/affine-shared/services';
import { focusTextModel } from '@blocksuite/affine-rich-text';
import {
EditPropsStore,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import type { NoteChildrenFlavour } from '@blocksuite/affine-shared/types';
import { hasClassNameInList } from '@blocksuite/affine-shared/utils';
import { Point } from '@blocksuite/global/gfx';
import type { PointerEventState } from '@blocksuite/std';
import { BaseTool } from '@blocksuite/std/gfx';
import {
handleNativeRangeAtPoint,
hasClassNameInList,
} from '@blocksuite/affine-shared/utils';
import { type IPoint, Point, serializeXYWH } from '@blocksuite/global/gfx';
import type { BlockStdScope, PointerEventState } from '@blocksuite/std';
import {
BaseTool,
type GfxBlockElementModel,
GfxControllerIdentifier,
} from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { DraggingNoteOverlay, NoteOverlay } from './overlay';
@@ -210,3 +227,116 @@ declare module '@blocksuite/std/gfx' {
'affine:note': NoteToolOption;
}
}
type NoteOptions = {
childFlavour: NoteChildrenFlavour;
childType: string | null;
collapse: boolean;
};
function addNote(
std: BlockStdScope,
point: Point,
options: NoteOptions,
width = DEFAULT_NOTE_WIDTH,
height = DEFAULT_NOTE_HEIGHT
) {
const noteId = addNoteAtPoint(std, point, {
width,
height,
});
const gfx = std.get(GfxControllerIdentifier);
const doc = std.store;
const blockId = doc.addBlock(
options.childFlavour,
{ type: options.childType },
noteId
);
if (options.collapse && height > NOTE_MIN_HEIGHT) {
const note = doc.getModelById(noteId) as NoteBlockModel;
doc.updateBlock(note, () => {
note.props.edgeless.collapse = true;
note.props.edgeless.collapsedHeight = height;
});
}
gfx.tool.setTool(DefaultTool);
// Wait for edgelessTool updated
requestAnimationFrame(() => {
const blocks =
(doc.root?.children.filter(
child => child.flavour === 'affine:note'
) as GfxBlockElementModel[]) ?? [];
const element = blocks.find(b => b.id === noteId);
if (element) {
gfx.selection.set({
elements: [element.id],
editing: true,
});
// Waiting dom updated, `note mask` is removed
if (blockId) {
focusTextModel(gfx.std, blockId);
} else {
// Cannot reuse `handleNativeRangeClick` directly here,
// since `retargetClick` will re-target to pervious editor
handleNativeRangeAtPoint(point.x, point.y);
}
}
});
}
function addNoteAtPoint(
std: BlockStdScope,
/**
* The point is in browser coordinate
*/
point: IPoint,
options: {
width?: number;
height?: number;
parentId?: string;
noteIndex?: number;
offsetX?: number;
offsetY?: number;
scale?: number;
} = {}
) {
const gfx = std.get(GfxControllerIdentifier);
const crud = std.get(EdgelessCRUDIdentifier);
const {
width = DEFAULT_NOTE_WIDTH,
height = DEFAULT_NOTE_HEIGHT,
offsetX = DEFAULT_NOTE_OFFSET_X,
offsetY = DEFAULT_NOTE_OFFSET_Y,
parentId = gfx.doc.root?.id,
noteIndex,
scale = 1,
} = options;
const [x, y] = gfx.viewport.toModelCoord(point.x, point.y);
const blockId = crud.addBlock(
'affine:note',
{
xywh: serializeXYWH(
x - offsetX * scale,
y - offsetY * scale,
width,
height
),
displayMode: NoteDisplayMode.EdgelessOnly,
},
parentId,
noteIndex
);
std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
control: 'canvas:draw',
page: 'whiteboard editor',
module: 'toolbar',
segment: 'toolbar',
type: 'note',
});
return blockId;
}

View File

@@ -5,10 +5,11 @@ import {
import { type Color, DefaultTheme } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import type { XYWH } from '@blocksuite/global/gfx';
import type { GfxController, GfxToolsMap } from '@blocksuite/std/gfx';
import type { GfxController } from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { Subject } from 'rxjs';
import type { NoteTool } from '../note-tool';
import {
NOTE_OVERLAY_CORNER_RADIUS,
NOTE_OVERLAY_HEIGHT,
@@ -33,8 +34,7 @@ export class NoteOverlay extends ToolOverlay {
effect(() => {
// when change note child type, update overlay text
if (this.gfx.tool.currentToolName$.value !== 'affine:note') return;
const tool =
this.gfx.tool.currentTool$.peek() as GfxToolsMap['affine:note'];
const tool = this.gfx.tool.currentTool$.peek() as NoteTool;
this.text = this._getOverlayText(tool.activatedOption.tip);
(this.gfx.surfaceComponent as SurfaceBlockComponent).refresh();
})

View File

@@ -1,6 +1,7 @@
import { addAttachments } from '@blocksuite/affine-block-attachment';
import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark';
import { addImages } from '@blocksuite/affine-block-image';
import { DefaultTool } from '@blocksuite/affine-block-surface';
import { MAX_IMAGE_WIDTH } from '@blocksuite/affine-model';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import type { NoteChildrenFlavour } from '@blocksuite/affine-shared/types';
@@ -10,13 +11,13 @@ import {
} from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { AttachmentIcon, ImageIcon, LinkIcon } from '@blocksuite/icons/lit';
import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import type { ToolOptions } from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { property, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import type { NoteToolOption } from '../note-tool.js';
import { NoteTool, type NoteToolOption } from '../note-tool.js';
import { NOTE_MENU_ITEMS } from './note-menu-config.js';
export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
@@ -51,7 +52,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
}
`;
override type: GfxToolsFullOptionValue['type'] = 'affine:note';
override type = NoteTool;
private async _addImages() {
this._imageLoading = true;
@@ -60,8 +61,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
maxWidth: MAX_IMAGE_WIDTH,
});
this._imageLoading = false;
// @ts-expect-error FIXME: resolve after gfx tool refactor
this.gfx.tool.setTool('default');
this.gfx.tool.setTool(DefaultTool);
this.gfx.selection.set({ elements: ids });
}
@@ -96,10 +96,11 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
effect(() => {
const tool = this.gfx.tool.currentToolOption$.value;
if (tool?.type !== 'affine:note') return;
this.childFlavour = tool.childFlavour;
this.childType = tool.childType;
this.tip = tool.tip;
if (tool?.toolType !== NoteTool) return;
const options = tool.options as ToolOptions<NoteTool>;
this.childFlavour = options.childFlavour;
this.childType = options.childType;
this.tip = options.tip;
})
);
}
@@ -141,8 +142,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
const file = await openFileOrFiles();
if (!file) return;
await addAttachments(this.edgeless.std, [file]);
// @ts-expect-error FIXME: resolve after gfx tool refactor
this.gfx.tool.setTool('default');
this.gfx.tool.setTool(DefaultTool);
this.edgeless.std
.getOptional(TelemetryProvider)
?.track('CanvasElementAdded', {

View File

@@ -13,7 +13,7 @@ import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { state } from 'lit/decorators.js';
import type { NoteToolOption } from '../note-tool.js';
import { NoteTool, type NoteToolOption } from '../note-tool.js';
import { toShapeNotToAdapt } from './icon.js';
export class EdgelessNoteSeniorButton extends EdgelessToolbarToolMixin(
@@ -138,15 +138,14 @@ export class EdgelessNoteSeniorButton extends EdgelessToolbarToolMixin(
override enableActiveBackground = true;
override type = 'affine:note' as const;
override type = NoteTool;
private _toggleNoteMenu() {
if (this.tryDisposePopper()) return;
const { edgeless, childFlavour, childType, tip } = this;
this.setEdgelessTool({
type: 'affine:note',
this.setEdgelessTool(NoteTool, {
childFlavour,
childType,
tip,
@@ -171,8 +170,7 @@ export class EdgelessNoteSeniorButton extends EdgelessToolbarToolMixin(
Object.assign(this, { [key]: props[key] });
}
});
this.setEdgelessTool({
type: 'affine:note',
this.setEdgelessTool(NoteTool, {
childFlavour: this.childFlavour,
childType: this.childType,
tip: this.tip,

View File

@@ -4,12 +4,11 @@ import {
QuickToolMixin,
} from '@blocksuite/affine-widget-edgeless-toolbar';
import { PageIcon } from '@blocksuite/icons/lit';
import type { GfxToolsFullOptionValue } from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { state } from 'lit/decorators.js';
import type { NoteToolOption } from '../note-tool.js';
import { NoteTool, type NoteToolOption } from '../note-tool.js';
import type { EdgelessNoteMenu } from './note-menu.js';
export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
@@ -23,7 +22,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
private readonly _states = ['childFlavour', 'childType', 'tip'] as const;
override type: GfxToolsFullOptionValue['type'] = 'affine:note';
override type = NoteTool;
private _disposeMenu() {
this._noteMenu?.dispose();
@@ -35,7 +34,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
this._disposeMenu();
this.requestUpdate();
} else {
this.gfx.tool.setTool('affine:note', {
this.gfx.tool.setTool(NoteTool, {
childFlavour: this.childFlavour,
childType: this.childType,
tip: this.tip,
@@ -59,7 +58,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
Object.assign(this, { [key]: props[key] });
}
});
this.gfx.tool.setTool('affine:note', {
this.gfx.tool.setTool(NoteTool, {
childFlavour: this.childFlavour,
childType: this.childType,
tip: this.tip,