refactor(editor): extract common mixin of edgeless toolbar (#11067)

This commit is contained in:
Saul-Mirone
2025-03-21 11:45:32 +00:00
parent 6777c16683
commit 35e986cb94
30 changed files with 167 additions and 147 deletions

View File

@@ -5,14 +5,13 @@ import {
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import type { ColorEvent } from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { SignalWatcher } from '@blocksuite/global/lit';
import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { property } from 'lit/decorators.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
export class EdgelessBrushMenu extends EdgelessToolbarToolMixin(
SignalWatcher(LitElement)
) {

View File

@@ -2,12 +2,12 @@ import {
EditPropsStore,
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit';
import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { styleMap } from 'lit/directives/style-map.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { EdgelessPenDarkIcon, EdgelessPenLightIcon } from './icons.js';
export class EdgelessBrushToolButton extends EdgelessToolbarToolMixin(

View File

@@ -3,6 +3,7 @@ import {
ThemeProvider,
ViewportElementProvider,
} from '@blocksuite/affine-shared/services';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { Bound } from '@blocksuite/global/gfx';
import {
type ReactiveController,
@@ -66,6 +67,10 @@ export class EdgelessDraggableElementController<T>
host.addController(this);
}
get gfx() {
return this.options.edgeless.std.get(GfxControllerIdentifier);
}
/**
* let overlay shape animate back to the original position
*/
@@ -146,8 +151,8 @@ export class EdgelessDraggableElementController<T>
private _onDragEnd() {
const { overlay, info, options } = this;
const { startTime, elementInfo, edgelessRect, validMoved } = info;
const { service, clickThreshold = 1500 } = options;
const zoom = service.viewport.zoom;
const { clickThreshold = 1500 } = options;
const zoom = this.gfx.viewport.zoom;
if (!validMoved) {
const duration = Date.now() - startTime;
@@ -168,7 +173,7 @@ export class EdgelessDraggableElementController<T>
if (this.states.dragOut && !this.states.cancelled && overlay) {
const rect = overlay.transitionWrapper.getBoundingClientRect();
const [modelX, modelY] = this.options.service.viewport.toModelCoord(
const [modelX, modelY] = this.gfx.viewport.toModelCoord(
rect.left - edgelessRect.left,
rect.top - edgelessRect.top
);
@@ -228,7 +233,7 @@ export class EdgelessDraggableElementController<T>
overlay.element.style.setProperty('--translate-x', `${offsetX}px`);
overlay.element.style.setProperty('--translate-y', `${offsetY}px`);
// - scale shape with scale
const zoom = options.service.viewport.zoom;
const zoom = this.gfx.viewport.zoom;
this._updateOverlayScale(zoom);
}
@@ -386,7 +391,7 @@ export class EdgelessDraggableElementController<T>
hostConnected() {
this.host.disposables.add(
this.options.service.viewport.viewportUpdated.subscribe(({ zoom }) => {
this.gfx.viewport.viewportUpdated.subscribe(({ zoom }) => {
this._updateOverlayScale(zoom);
})
);

View File

@@ -1,10 +1,8 @@
import type { BlockComponent } from '@blocksuite/block-std';
import type { Bound } from '@blocksuite/global/gfx';
import type { DisposableClass } from '@blocksuite/global/lit';
import type { TemplateResult } from 'lit';
import type { EdgelessRootBlockComponent } from '../../../../edgeless-root-block.js';
import type { EdgelessRootService } from '../../../../edgeless-root-service.js';
export interface EdgelessDraggableElementHost extends DisposableClass {}
export interface OverlayLayer {
@@ -25,8 +23,7 @@ export interface OverlayLayer {
}
export interface EdgelessDraggableElementOptions<T> {
edgeless: EdgelessRootBlockComponent;
service: EdgelessRootService;
edgeless: BlockComponent;
/**
* In which element that the target should be dragged out
* If not provided, recognized as the drag-out whenever dragging

View File

@@ -9,6 +9,7 @@ import {
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import type { ColorEvent } from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { SignalWatcher } from '@blocksuite/global/lit';
import {
@@ -20,8 +21,6 @@ import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { property } from 'lit/decorators.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
function ConnectorModeButtonGroup(
mode: ConnectorMode,
setConnectorMode: (props: Record<string, unknown>) => void

View File

@@ -1,5 +1,6 @@
import { ConnectorMode, getConnectorModeName } from '@blocksuite/affine-model';
import { EditPropsStore } from '@blocksuite/affine-shared/services';
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit';
import {
ConnectorCIcon,
@@ -9,8 +10,6 @@ import {
import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
const IcomMap = {
[ConnectorMode.Straight]: ConnectorLIcon(),
[ConnectorMode.Orthogonal]: ConnectorEIcon(),
@@ -65,7 +64,7 @@ export class EdgelessConnectorToolButton extends QuickToolMixin(
@click=${() => {
// don't update tool before toggling menu
this._toggleMenu();
this.edgeless.gfx.tool.setTool('connector', {
this.gfx.tool.setTool('connector', {
mode,
});
}}

View File

@@ -1,10 +1,10 @@
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { HandIcon, SelectIcon } from '@blocksuite/icons/lit';
import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { query } from 'lit/decorators.js';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
export class EdgelessDefaultToolButton extends QuickToolMixin(LitElement) {
static override styles = css`
.current-icon {
@@ -62,7 +62,7 @@ export class EdgelessDefaultToolButton extends QuickToolMixin(LitElement) {
}
this.disposables.add(
effect(() => {
const tool = this.edgeless.gfx.tool.currentToolName$.value;
const tool = this.gfx.tool.currentToolName$.value;
if (tool === 'default' || tool === 'pan') {
localStorage.defaultTool = tool;
}

View File

@@ -1,8 +1,8 @@
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { css, html, LitElement } from 'lit';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { EdgelessEraserDarkIcon, EdgelessEraserLightIcon } from './icons.js';
export class EdgelessEraserToolButton extends EdgelessToolbarToolMixin(

View File

@@ -1,8 +1,9 @@
import { EdgelessFrameManagerIdentifier } from '@blocksuite/affine-block-frame';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { css, html, LitElement } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { FrameConfig } from './config.js';
export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
@@ -66,8 +67,12 @@ export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
override type: GfxToolsFullOptionValue['type'] = 'frame';
get frameManager() {
return this.edgeless.std.get(EdgelessFrameManagerIdentifier);
}
override render() {
const { edgeless } = this;
const { gfx, frameManager } = this;
return html`
<edgeless-slide-menu .showNext=${false}>
<div class="menu-content">
@@ -79,8 +84,8 @@ export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
(item, index) => html`
<div
@click=${() => {
edgeless.gfx.tool.setTool('default');
edgeless.service.frame.createFrameOnViewportCenter(item.wh);
gfx.tool.setTool('default');
frameManager.createFrameOnViewportCenter(item.wh);
}}
class="frame-add-button ${index}"
data-name="${item.name}"

View File

@@ -1,9 +1,8 @@
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { FrameIcon } from '@blocksuite/icons/lit';
import { css, html, LitElement } from 'lit';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
export class EdgelessFrameToolButton extends QuickToolMixin(LitElement) {
static override styles = css`
:host {

View File

@@ -1,10 +1,10 @@
import { LassoMode } from '@blocksuite/affine-shared/types';
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { WithDisposable } from '@blocksuite/global/lit';
import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { query, state } from 'lit/decorators.js';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
import { LassoFreeHandIcon, LassoPolygonalIcon } from './icons.js';
export class EdgelessLassoToolButton extends QuickToolMixin(
@@ -54,7 +54,7 @@ export class EdgelessLassoToolButton extends QuickToolMixin(
this.disposables.add(
effect(() => {
const tool = this.edgeless.gfx.tool.currentToolOption$.value;
const tool = this.gfx.tool.currentToolOption$.value;
if (tool?.type === 'lasso') {
const { mode } = tool;

View File

@@ -1,10 +1,9 @@
import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark';
import { LinkIcon } from '@blocksuite/affine-components/icons';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { css, html, LitElement } from 'lit';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
export class EdgelessLinkToolButton extends QuickToolMixin(LitElement) {
static override styles = css`
.link-icon,

View File

@@ -1,7 +1,10 @@
import { addAttachments } from '@blocksuite/affine-block-attachment';
import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text';
import { addImages } from '@blocksuite/affine-block-image';
import { CanvasElementType } from '@blocksuite/affine-block-surface';
import {
CanvasElementType,
EdgelessCRUDIdentifier,
} from '@blocksuite/affine-block-surface';
import { mountTextElementEditor } from '@blocksuite/affine-gfx-text';
import {
MAX_IMAGE_WIDTH,
@@ -13,13 +16,11 @@ import {
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import { openFileOrFiles } from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std';
import { Bound } from '@blocksuite/global/gfx';
import type { TemplateResult } from 'lit';
import * as Y from 'yjs';
import type { EdgelessRootBlockComponent } from '../../../edgeless-root-block.js';
import type { EdgelessRootService } from '../../../edgeless-root-service.js';
export type ConfigProperty = 'x' | 'y' | 'r' | 's' | 'z' | 'o';
export type ConfigState = 'default' | 'active' | 'hover' | 'next';
export type ConfigStyle = Partial<Record<ConfigProperty, number | string>>;
@@ -30,11 +31,7 @@ export type DraggableTool = {
icon: TemplateResult;
config: ToolConfig;
standardWidth?: number;
render: (
bound: Bound,
edgelessService: EdgelessRootService,
edgeless: EdgelessRootBlockComponent
) => Promise<string | null>;
render: (bound: Bound, edgeless: BlockComponent) => Promise<string | null>;
};
const unitMap = { x: 'px', y: 'px', r: 'deg', s: '', z: '', o: '' };
@@ -59,7 +56,7 @@ export const mediaConfig: ToolConfig = {
export const getMindmapRender =
(mindmapStyle: MindmapStyle): DraggableTool['render'] =>
async (bound, edgelessService) => {
async (bound, edgeless) => {
const [x, y, _, h] = bound.toXYWH();
const rootW = 145;
@@ -94,29 +91,24 @@ export const getMindmapRender =
});
}
const mindmapId = edgelessService.crud.addElement('mindmap', {
const crud = edgeless.std.get(EdgelessCRUDIdentifier);
const mindmapId = crud.addElement('mindmap', {
style: mindmapStyle,
children: root,
}) as string;
edgelessService.std
.getOptional(TelemetryProvider)
?.track('CanvasElementAdded', {
control: 'toolbar:dnd', // for now we use toolbar:dnd for all mindmap creation here
page: 'whiteboard editor',
module: 'toolbar',
segment: 'toolbar',
type: 'mindmap',
});
edgeless.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
control: 'toolbar:dnd', // for now we use toolbar:dnd for all mindmap creation here
page: 'whiteboard editor',
module: 'toolbar',
segment: 'toolbar',
type: 'mindmap',
});
return mindmapId;
};
export const textRender: DraggableTool['render'] = async (
bound,
service,
edgeless
) => {
export const textRender: DraggableTool['render'] = async (bound, edgeless) => {
const vCenter = bound.y + bound.h / 2;
const w = 100;
const h = 32;
@@ -135,13 +127,14 @@ export const textRender: DraggableTool['render'] = async (
);
id = textId!;
} else {
id = service.crud.addElement(CanvasElementType.TEXT, {
const crud = edgeless.std.get(EdgelessCRUDIdentifier);
id = crud.addElement(CanvasElementType.TEXT, {
xywh: new Bound(bound.x, vCenter - h / 2, w, h).serialize(),
text: new Y.Text(),
}) as string;
edgeless.doc.captureSync();
const textElement = edgeless.service.crud.getElementById(id);
const textElement = crud.getElementById(id);
if (!(textElement instanceof TextElementModel)) {
console.error('Cannot mount text editor on a non-text element');
return null;
@@ -149,7 +142,7 @@ export const textRender: DraggableTool['render'] = async (
mountTextElementEditor(textElement, edgeless);
}
service.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
edgeless.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
control: 'toolbar:dnd',
page: 'whiteboard editor',
module: 'toolbar',
@@ -160,11 +153,7 @@ export const textRender: DraggableTool['render'] = async (
return id;
};
export const mediaRender: DraggableTool['render'] = async (
bound,
_,
edgeless
) => {
export const mediaRender: DraggableTool['render'] = async (bound, edgeless) => {
let file: File | null = null;
try {
file = await openFileOrFiles();

View File

@@ -5,6 +5,7 @@ import {
FeatureFlagService,
TelemetryProvider,
} from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { BlockStdScope } from '@blocksuite/block-std';
import { modelContext, stdContext } from '@blocksuite/block-std';
import { ErrorCode } from '@blocksuite/global/exceptions';
@@ -19,7 +20,6 @@ import { repeat } from 'lit/directives/repeat.js';
import type { EdgelessRootBlockComponent } from '../../../index.js';
import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { getMindMaps, type ToolbarMindmapItem } from './assets.js';
import { mediaRender, textRender } from './basket-elements.js';
import { importMindMapIcon, mindmapMenuMediaIcon, textIcon } from './icons.js';
@@ -220,7 +220,6 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin(
initDragController() {
if (this.draggableController || !this.edgeless) return;
this.draggableController = new EdgelessDraggableElementController(this, {
service: this.edgeless.service,
edgeless: this.edgeless,
scopeElement: this,
clickToDrag: true,
@@ -234,13 +233,13 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin(
onDrop: (element, bound) => {
if ('render' in element.data) {
element.data
.render(bound, this.edgeless.service, this.edgeless)
.render(bound, this.edgeless)
.then(id => {
if (!id) return;
if (element.data.type === 'mindmap') {
this.onActiveStyleChange?.(element.data.style);
this.setEdgelessTool({ type: 'default' });
this.edgeless.gfx.selection.set({
this.gfx.selection.set({
elements: [id],
editing: false,
});

View File

@@ -8,6 +8,7 @@ import {
ThemeProvider,
ViewportElementProvider,
} from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import type { Bound } from '@blocksuite/global/gfx';
import { SignalWatcher } from '@blocksuite/global/lit';
@@ -19,7 +20,6 @@ import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { getMindMaps } from './assets.js';
import {
type DraggableTool,
@@ -210,7 +210,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
this.tryDisposePopper();
this.setEdgelessTool({ type: 'default' });
this.edgeless.gfx.selection.set({
this.gfx.selection.set({
elements: [element.tree.id],
editing: false,
});
@@ -223,7 +223,6 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
if (!this.edgeless || !this.toolbarContainer) return;
if (this.draggableController) return;
this.draggableController = new EdgelessDraggableElementController(this, {
service: this.edgeless.service,
edgeless: this.edgeless,
scopeElement: this.toolbarContainer,
standardWidth: 100,
@@ -265,13 +264,13 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
},
onDrop: (el, bound) => {
el.data
.render(bound, this.edgeless.service, this.edgeless)
.render(bound, this.edgeless)
.then(id => {
if (!id) return;
this.readyToDrop = false;
if (el.data.name === 'mindmap') {
this.setEdgelessTool({ type: 'default' });
this.edgeless.gfx.selection.set({
this.gfx.selection.set({
elements: [id],
editing: false,
});
@@ -286,9 +285,10 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
this.edgeless.bindHotKey(
{
m: () => {
const service = this.edgeless.service;
if (service.locked) return;
if (service.selection.editing) return;
const gfx = this.gfx;
const locked = gfx.viewport.locked;
if (locked) return;
if (gfx.selection.editing) return;
if (this.readyToDrop) {
// change the style
@@ -309,7 +309,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
}
this.setEdgelessTool({ type: 'empty' });
const icon = this.mindmapElement;
const { x, y } = service.gfx.tool.lastMousePos$.peek();
const { x, y } = gfx.tool.lastMousePos$.peek();
const { viewport } = this.edgeless.std.get(ViewportElementProvider);
const { left, top } = viewport;
const clientPos = { x: x + left, y: y + top };

View File

@@ -9,6 +9,7 @@ import {
getImageFilesFromLocal,
openFileOrFiles,
} from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
@@ -16,7 +17,6 @@ import { property, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import type { NoteToolOption } from '../../../gfx-tool/note-tool.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { NOTE_MENU_ITEMS } from './note-menu-config.js';
export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
@@ -60,8 +60,8 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
maxWidth: MAX_IMAGE_WIDTH,
});
this._imageLoading = false;
this.edgeless.gfx.tool.setTool('default');
this.edgeless.gfx.selection.set({ elements: ids });
this.gfx.tool.setTool('default');
this.gfx.selection.set({ elements: ids });
}
private _onHandleLinkButtonClick() {
@@ -93,7 +93,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
override firstUpdated() {
this.disposables.add(
effect(() => {
const tool = this.edgeless.gfx.tool.currentToolOption$.value;
const tool = this.gfx.tool.currentToolOption$.value;
if (tool?.type !== 'affine:note') return;
this.childFlavour = tool.childFlavour;
@@ -140,7 +140,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
const file = await openFileOrFiles();
if (!file) return;
await addAttachments(this.edgeless.std, [file]);
this.edgeless.gfx.tool.setTool('default');
this.gfx.tool.setTool('default');
this.edgeless.std
.getOptional(TelemetryProvider)
?.track('CanvasElementAdded', {

View File

@@ -7,13 +7,13 @@ import {
EditPropsStore,
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit';
import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { state } from 'lit/decorators.js';
import type { NoteToolOption } from '../../../gfx-tool/note-tool.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { toShapeNotToAdapt } from './icon.js';
export class EdgelessNoteSeniorButton extends EdgelessToolbarToolMixin(

View File

@@ -1,6 +1,7 @@
import {
createPopper,
type MenuPopper,
QuickToolMixin,
} from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { PageIcon } from '@blocksuite/icons/lit';
@@ -9,7 +10,6 @@ import { css, html, LitElement } from 'lit';
import { state } from 'lit/decorators.js';
import type { NoteToolOption } from '../../../gfx-tool/note-tool.js';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
import type { EdgelessNoteMenu } from './note-menu.js';
export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
@@ -35,7 +35,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
this._disposeMenu();
this.requestUpdate();
} else {
this.edgeless.gfx.tool.setTool('affine:note', {
this.gfx.tool.setTool('affine:note', {
childFlavour: this.childFlavour,
childType: this.childType,
tip: this.tip,
@@ -59,7 +59,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
Object.assign(this, { [key]: props[key] });
}
});
this.edgeless.gfx.tool.setTool('affine:note', {
this.gfx.tool.setTool('affine:note', {
childFlavour: this.childFlavour,
childType: this.childType,
tip: this.tip,
@@ -72,7 +72,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
super.connectedCallback();
this._disposables.add(
effect(() => {
const value = this.edgeless.gfx.tool.currentToolName$.value;
const value = this.gfx.tool.currentToolName$.value;
if (value !== 'affine:note') {
this._disposeMenu();
}

View File

@@ -1,10 +1,11 @@
import {
EdgelessToolbarToolMixin,
QuickToolMixin,
} from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { PresentationIcon } from '@blocksuite/icons/lit';
import { css, html, LitElement } from 'lit';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
export class EdgelessPresentButton extends QuickToolMixin(
EdgelessToolbarToolMixin(LitElement)
) {

View File

@@ -1,10 +1,16 @@
import {
EdgelessFrameManagerIdentifier,
isFrameBlock,
type NavigatorMode,
} from '@blocksuite/affine-block-frame';
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
import { toast } from '@blocksuite/affine-components/toast';
import type { FrameBlockModel } from '@blocksuite/affine-model';
import { EditPropsStore } from '@blocksuite/affine-shared/services';
import {
EditPropsStore,
ViewportElementProvider,
} from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { Bound, clamp } from '@blocksuite/global/gfx';
import { SignalWatcher } from '@blocksuite/global/lit';
@@ -22,7 +28,6 @@ import { property, state } from 'lit/decorators.js';
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
import { launchIntoFullscreen } from '../utils.js';
import { EdgelessToolbarToolMixin } from './mixins/tool.mixin.js';
export class PresentationToolbar extends EdgelessToolbarToolMixin(
SignalWatcher(LitElement)
@@ -125,7 +130,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
}
private get _frames(): FrameBlockModel[] {
return this.edgeless.service.frames;
return this.edgeless.std.get(EdgelessFrameManagerIdentifier).frames;
}
get dense() {
@@ -136,6 +141,10 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
return this.edgeless.host;
}
get slots() {
return this.edgeless.std.get(EdgelessLegacySlotIdentifier);
}
constructor(edgeless: EdgelessRootBlockComponent) {
super();
this.edgeless = edgeless;
@@ -176,7 +185,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
private _moveToCurrentFrame() {
const current = this._currentFrameIndex;
const viewport = this.edgeless.service.viewport;
const viewport = this.gfx.viewport;
const frame = this._frames[current];
if (frame) {
@@ -197,7 +206,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
}
viewport.setViewportByBound(bound, [0, 0, 0, 0], false);
this.edgeless.slots.navigatorFrameChanged.next(
this.slots.navigatorFrameChanged.next(
this._frames[this._currentFrameIndex]
);
}
@@ -235,7 +244,10 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
document.exitFullscreen().catch(console.error);
this._fullScreenMode = false;
} else {
launchIntoFullscreen(this.edgeless.viewportElement);
const { viewportElement } = this.edgeless.std.get(
ViewportElementProvider
);
launchIntoFullscreen(viewportElement);
this._fullScreenMode = true;
}
}
@@ -243,19 +255,19 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
override connectedCallback(): void {
super.connectedCallback();
const { _disposables, edgeless } = this;
const { _disposables } = this;
_disposables.add(
effect(() => {
const currentTool = this.edgeless.gfx.tool.currentToolOption$.value;
const currentTool = this.gfx.tool.currentToolOption$.value;
const selection = this.gfx.selection;
if (currentTool?.type === 'frameNavigator') {
this._cachedIndex = this._currentFrameIndex;
this._navigatorMode = currentTool.mode ?? this._navigatorMode;
if (isFrameBlock(edgeless.service.selection.selectedElements[0])) {
if (isFrameBlock(selection.selectedElements[0])) {
this._cachedIndex = this._frames.findIndex(
frame =>
frame.id === edgeless.service.selection.selectedElements[0].id
frame => frame.id === selection.selectedElements[0].id
);
}
if (this._frames.length === 0)
@@ -273,12 +285,12 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
}
override firstUpdated() {
const { _disposables, edgeless } = this;
const { _disposables } = this;
this._bindHotKey();
_disposables.add(
edgeless.slots.navigatorSettingUpdated.subscribe(({ fillScreen }) => {
this.slots.navigatorSettingUpdated.subscribe(({ fillScreen }) => {
if (fillScreen !== undefined) {
this._navigatorMode = fillScreen ? 'fill' : 'fit';
}
@@ -307,7 +319,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
}
setTimeout(() => this._moveToCurrentFrame(), 400);
this.edgeless.slots.fullScreenToggled.next();
this.slots.fullScreenToggled.next();
});
this._navigatorMode =

View File

@@ -19,6 +19,7 @@ import {
ThemeProvider,
ViewportElementProvider,
} from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit';
import { css, html, LitElement, nothing } from 'lit';
import { property, query, state } from 'lit/decorators.js';
@@ -27,7 +28,6 @@ import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import type { DraggableShape } from './utils.js';
import { buildVariablesObject } from './utils.js';
@@ -152,7 +152,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
}
private _setShapeOverlayLock(lock: boolean) {
const controller = this.edgeless.gfx.tool.currentTool$.peek();
const controller = this.gfx.tool.currentTool$.peek();
if (controller instanceof ShapeTool) {
controller.setDisableOverlay(lock);
}
@@ -162,7 +162,6 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
if (!this.edgeless || !this.toolbarContainer) return;
if (this.draggableController) return;
this.draggableController = new EdgelessDraggableElementController(this, {
service: this.edgeless.service,
edgeless: this.edgeless,
scopeElement: this.toolbarContainer,
standardWidth: 100,
@@ -176,7 +175,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
type: 'shape',
shapeName,
});
const controller = this.edgeless.gfx.tool.currentTool$.peek();
const controller = this.gfx.tool.currentTool$.peek();
if (controller instanceof ShapeTool) {
controller.clearOverlay();
}
@@ -209,8 +208,8 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
this._setShapeOverlayLock(false);
this.readyToDrop = false;
this.edgeless.gfx.tool.setTool('default');
this.edgeless.gfx.selection.set({
this.gfx.tool.setTool('default');
this.gfx.selection.set({
elements: [id],
editing: false,
});
@@ -236,8 +235,9 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
// `page.keyboard.press('Shift+s')` in playwright will also trigger this 's' key event
if (ctx.get('keyboardState').raw.shiftKey) return;
const service = this.edgeless.service;
if (service.locked || service.selection.editing) return;
const locked = this.gfx.viewport.locked;
const selection = this.gfx.selection;
if (locked || selection.editing) return;
if (this.readyToDrop) {
const activeIndex = shapes.findIndex(
@@ -257,7 +257,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
console.error('Edgeless toolbar Shape element not found');
return;
}
const { x, y } = service.gfx.tool.lastMousePos$.peek();
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 };

View File

@@ -1,9 +1,9 @@
import { ShapeTool } from '@blocksuite/affine-gfx-shape';
import { type ShapeName, ShapeType } from '@blocksuite/affine-model';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit';
import { css, html, LitElement } from 'lit';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import type { DraggableShape } from './utils.js';
export class EdgelessShapeToolButton extends EdgelessToolbarToolMixin(
@@ -55,7 +55,7 @@ export class EdgelessShapeToolButton extends EdgelessToolbarToolMixin(
}
private _updateOverlay() {
const controller = this.edgeless.gfx.tool.currentTool$.peek();
const controller = this.gfx.tool.currentTool$.peek();
if (controller instanceof ShapeTool) {
controller.createOverlay();
}

View File

@@ -10,6 +10,8 @@ import {
requestConnectedFrame,
stopPropagation,
} from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import type { Bound } from '@blocksuite/global/gfx';
import { WithDisposable } from '@blocksuite/global/lit';
import { baseTheme } from '@toeverything/theme';
@@ -19,7 +21,7 @@ import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
import type { EdgelessRootBlockComponent } from '../../../edgeless-root-block.js';
import { EdgelessRootService } from '../../../edgeless-root-service.js';
import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js';
import { builtInTemplates } from './builtin-templates.js';
import { defaultPreview, Triangle } from './cards.js';
@@ -252,7 +254,6 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
private _initDragController() {
if (this.draggableController) return;
this.draggableController = new EdgelessDraggableElementController(this, {
service: this.edgeless.service,
edgeless: this.edgeless,
clickToDrag: true,
standardWidth: 560,
@@ -273,6 +274,14 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
});
}
get service() {
return this.edgeless.std.get(EdgelessRootService);
}
get gfx() {
return this.edgeless.std.get(GfxControllerIdentifier);
}
private async _insertTemplate(template: Template, bound: Bound) {
this._loadingTemplate = template;
@@ -282,11 +291,7 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
x: bound.x + bound.w / 2,
y: bound.y + bound.h / 2,
};
const templateJob = this.edgeless.service.createTemplateJob(
template.type,
center
);
const service = this.edgeless.service;
const templateJob = this.service.createTemplateJob(template.type, center);
try {
const { assets } = template;
@@ -304,8 +309,8 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
const insertedBound = await templateJob.insertTemplate(template.content);
if (insertedBound && template.type === 'template') {
const padding = 20 / service.viewport.zoom;
service.viewport.setViewportByBound(
const padding = 20 / this.gfx.viewport.zoom;
this.gfx.viewport.setViewportByBound(
insertedBound,
[padding, padding, padding, padding],
true
@@ -313,7 +318,7 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
}
} finally {
this._loadingTemplate = null;
this.edgeless.gfx.tool.setTool('default');
this.gfx.tool.setTool('default');
}
}
@@ -507,7 +512,7 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
private accessor _templates: Template[] = [];
@property({ attribute: false })
accessor edgeless!: EdgelessRootBlockComponent;
accessor edgeless!: BlockComponent;
@state()
accessor isDragging = false;

View File

@@ -1,5 +1,6 @@
import { ArrowDownSmallIcon } from '@blocksuite/affine-components/icons';
import { once } from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import {
arrow,
@@ -13,7 +14,6 @@ import { state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { repeat } from 'lit/directives/repeat.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { TemplateCard1, TemplateCard2, TemplateCard3 } from './cards.js';
import type { EdgelessTemplatePanel } from './template-panel.js';

View File

@@ -1,13 +1,12 @@
import { DefaultTheme } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services';
import type { ColorEvent } from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { computed } from '@preact/signals-core';
import { css, html, LitElement, nothing } from 'lit';
import { property } from 'lit/decorators.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
export class EdgelessTextMenu extends EdgelessToolbarToolMixin(LitElement) {
static override styles = css`
:host {

View File

@@ -2,3 +2,4 @@ export * from './context';
export * from './create-popper';
export * from './edgeless-toolbar';
export * from './extension';
export * from './mixins';

View File

@@ -0,0 +1,3 @@
export * from './quick-tool.mixin';
export * from './tool.mixin';
export * from './toolbar-button-with-menu.mixin';

View File

@@ -1,17 +1,11 @@
import type { ColorScheme } from '@blocksuite/affine-model';
import type { BlockComponent } from '@blocksuite/block-std';
import {
createPopper,
edgelessToolbarContext,
type EdgelessToolbarSlots,
edgelessToolbarSlotsContext,
edgelessToolbarThemeContext,
type EdgelessToolbarWidget,
type MenuPopper,
} from '@blocksuite/affine-widget-edgeless-toolbar';
import type {
GfxToolsFullOption,
GfxToolsFullOptionValue,
ToolController,
type GfxController,
GfxControllerIdentifier,
type GfxToolsFullOption,
type GfxToolsFullOptionValue,
type ToolController,
} from '@blocksuite/block-std/gfx';
import {
// oxlint-disable-next-line no-unused-vars
@@ -25,7 +19,14 @@ import { cssVar } from '@toeverything/theme';
import type { LitElement } from 'lit';
import { property, state } from 'lit/decorators.js';
import type { EdgelessRootBlockComponent } from '../../../edgeless-root-block.js';
import {
edgelessToolbarContext,
type EdgelessToolbarSlots,
edgelessToolbarSlotsContext,
edgelessToolbarThemeContext,
} from '../context';
import { createPopper, type MenuPopper } from '../create-popper';
import type { EdgelessToolbarWidget } from '../edgeless-toolbar';
type ValueOf<T> = T[keyof T];
@@ -34,7 +35,7 @@ export declare abstract class EdgelessToolbarToolClass extends DisposableClass {
createPopper: typeof createPopper;
edgeless: EdgelessRootBlockComponent;
edgeless: BlockComponent;
edgelessTool: GfxToolsFullOptionValue;
@@ -44,6 +45,8 @@ export declare abstract class EdgelessToolbarToolClass extends DisposableClass {
setEdgelessTool: ToolController['setTool'];
gfx: GfxController;
theme: ColorScheme;
toolbarContainer: HTMLElement | null;
@@ -74,18 +77,24 @@ export const EdgelessToolbarToolMixin = <T extends Constructor<LitElement>>(
get active() {
const { type } = this;
// @ts-expect-error FIXME: we need to fix the type of edgelessTool
const activeType = this.edgelessTool?.type;
return activeType
? Array.isArray(type)
? type.includes(activeType)
? // @ts-expect-error FIXME: we need to fix the type of edgelessTool
type.includes(activeType)
: activeType === type
: false;
}
get gfx() {
return this.edgeless.std.get(GfxControllerIdentifier);
}
get setEdgelessTool() {
return (...args: Parameters<ToolController['setTool']>) => {
this.edgeless.gfx.tool.setTool(
this.gfx.tool.setTool(
// @ts-expect-error FIXME: ts error
...args
);
@@ -100,7 +109,7 @@ export const EdgelessToolbarToolMixin = <T extends Constructor<LitElement>>(
}
private _updateActiveEdgelessTool() {
this.edgelessTool = this.edgeless.gfx.tool.currentToolOption$.value;
this.edgelessTool = this.gfx.tool.currentToolOption$.value;
this._applyActiveStyle();
}
@@ -150,7 +159,7 @@ export const EdgelessToolbarToolMixin = <T extends Constructor<LitElement>>(
}
@property({ attribute: false })
accessor edgeless!: EdgelessRootBlockComponent;
accessor edgeless!: BlockComponent;
@state()
accessor edgelessTool!: ValueOf<GfxToolsFullOption> | null;