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
@@ -5,14 +5,13 @@ import {
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import type { ColorEvent } from '@blocksuite/affine-shared/utils'; 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 type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { SignalWatcher } from '@blocksuite/global/lit'; import { SignalWatcher } from '@blocksuite/global/lit';
import { computed } from '@preact/signals-core'; import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { property } from 'lit/decorators.js'; import { property } from 'lit/decorators.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
export class EdgelessBrushMenu extends EdgelessToolbarToolMixin( export class EdgelessBrushMenu extends EdgelessToolbarToolMixin(
SignalWatcher(LitElement) SignalWatcher(LitElement)
) { ) {
@@ -2,12 +2,12 @@ import {
EditPropsStore, EditPropsStore,
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit'; import { SignalWatcher } from '@blocksuite/global/lit';
import { computed } from '@preact/signals-core'; import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { EdgelessPenDarkIcon, EdgelessPenLightIcon } from './icons.js'; import { EdgelessPenDarkIcon, EdgelessPenLightIcon } from './icons.js';
export class EdgelessBrushToolButton extends EdgelessToolbarToolMixin( export class EdgelessBrushToolButton extends EdgelessToolbarToolMixin(
@@ -3,6 +3,7 @@ import {
ThemeProvider, ThemeProvider,
ViewportElementProvider, ViewportElementProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { Bound } from '@blocksuite/global/gfx'; import { Bound } from '@blocksuite/global/gfx';
import { import {
type ReactiveController, type ReactiveController,
@@ -66,6 +67,10 @@ export class EdgelessDraggableElementController<T>
host.addController(this); host.addController(this);
} }
get gfx() {
return this.options.edgeless.std.get(GfxControllerIdentifier);
}
/** /**
* let overlay shape animate back to the original position * let overlay shape animate back to the original position
*/ */
@@ -146,8 +151,8 @@ export class EdgelessDraggableElementController<T>
private _onDragEnd() { private _onDragEnd() {
const { overlay, info, options } = this; const { overlay, info, options } = this;
const { startTime, elementInfo, edgelessRect, validMoved } = info; const { startTime, elementInfo, edgelessRect, validMoved } = info;
const { service, clickThreshold = 1500 } = options; const { clickThreshold = 1500 } = options;
const zoom = service.viewport.zoom; const zoom = this.gfx.viewport.zoom;
if (!validMoved) { if (!validMoved) {
const duration = Date.now() - startTime; const duration = Date.now() - startTime;
@@ -168,7 +173,7 @@ export class EdgelessDraggableElementController<T>
if (this.states.dragOut && !this.states.cancelled && overlay) { if (this.states.dragOut && !this.states.cancelled && overlay) {
const rect = overlay.transitionWrapper.getBoundingClientRect(); 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.left - edgelessRect.left,
rect.top - edgelessRect.top 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-x', `${offsetX}px`);
overlay.element.style.setProperty('--translate-y', `${offsetY}px`); overlay.element.style.setProperty('--translate-y', `${offsetY}px`);
// - scale shape with scale // - scale shape with scale
const zoom = options.service.viewport.zoom; const zoom = this.gfx.viewport.zoom;
this._updateOverlayScale(zoom); this._updateOverlayScale(zoom);
} }
@@ -386,7 +391,7 @@ export class EdgelessDraggableElementController<T>
hostConnected() { hostConnected() {
this.host.disposables.add( this.host.disposables.add(
this.options.service.viewport.viewportUpdated.subscribe(({ zoom }) => { this.gfx.viewport.viewportUpdated.subscribe(({ zoom }) => {
this._updateOverlayScale(zoom); this._updateOverlayScale(zoom);
}) })
); );
@@ -1,10 +1,8 @@
import type { BlockComponent } from '@blocksuite/block-std';
import type { Bound } from '@blocksuite/global/gfx'; import type { Bound } from '@blocksuite/global/gfx';
import type { DisposableClass } from '@blocksuite/global/lit'; import type { DisposableClass } from '@blocksuite/global/lit';
import type { TemplateResult } from '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 EdgelessDraggableElementHost extends DisposableClass {}
export interface OverlayLayer { export interface OverlayLayer {
@@ -25,8 +23,7 @@ export interface OverlayLayer {
} }
export interface EdgelessDraggableElementOptions<T> { export interface EdgelessDraggableElementOptions<T> {
edgeless: EdgelessRootBlockComponent; edgeless: BlockComponent;
service: EdgelessRootService;
/** /**
* In which element that the target should be dragged out * In which element that the target should be dragged out
* If not provided, recognized as the drag-out whenever dragging * If not provided, recognized as the drag-out whenever dragging
@@ -9,6 +9,7 @@ import {
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import type { ColorEvent } from '@blocksuite/affine-shared/utils'; 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 type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { SignalWatcher } from '@blocksuite/global/lit'; import { SignalWatcher } from '@blocksuite/global/lit';
import { import {
@@ -20,8 +21,6 @@ import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { property } from 'lit/decorators.js'; import { property } from 'lit/decorators.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
function ConnectorModeButtonGroup( function ConnectorModeButtonGroup(
mode: ConnectorMode, mode: ConnectorMode,
setConnectorMode: (props: Record<string, unknown>) => void setConnectorMode: (props: Record<string, unknown>) => void
@@ -1,5 +1,6 @@
import { ConnectorMode, getConnectorModeName } from '@blocksuite/affine-model'; import { ConnectorMode, getConnectorModeName } from '@blocksuite/affine-model';
import { EditPropsStore } from '@blocksuite/affine-shared/services'; import { EditPropsStore } from '@blocksuite/affine-shared/services';
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit'; import { SignalWatcher } from '@blocksuite/global/lit';
import { import {
ConnectorCIcon, ConnectorCIcon,
@@ -9,8 +10,6 @@ import {
import { computed } from '@preact/signals-core'; import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
const IcomMap = { const IcomMap = {
[ConnectorMode.Straight]: ConnectorLIcon(), [ConnectorMode.Straight]: ConnectorLIcon(),
[ConnectorMode.Orthogonal]: ConnectorEIcon(), [ConnectorMode.Orthogonal]: ConnectorEIcon(),
@@ -65,7 +64,7 @@ export class EdgelessConnectorToolButton extends QuickToolMixin(
@click=${() => { @click=${() => {
// don't update tool before toggling menu // don't update tool before toggling menu
this._toggleMenu(); this._toggleMenu();
this.edgeless.gfx.tool.setTool('connector', { this.gfx.tool.setTool('connector', {
mode, mode,
}); });
}} }}
@@ -1,10 +1,10 @@
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx'; import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { HandIcon, SelectIcon } from '@blocksuite/icons/lit'; import { HandIcon, SelectIcon } from '@blocksuite/icons/lit';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { query } from 'lit/decorators.js'; import { query } from 'lit/decorators.js';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
export class EdgelessDefaultToolButton extends QuickToolMixin(LitElement) { export class EdgelessDefaultToolButton extends QuickToolMixin(LitElement) {
static override styles = css` static override styles = css`
.current-icon { .current-icon {
@@ -62,7 +62,7 @@ export class EdgelessDefaultToolButton extends QuickToolMixin(LitElement) {
} }
this.disposables.add( this.disposables.add(
effect(() => { effect(() => {
const tool = this.edgeless.gfx.tool.currentToolName$.value; const tool = this.gfx.tool.currentToolName$.value;
if (tool === 'default' || tool === 'pan') { if (tool === 'default' || tool === 'pan') {
localStorage.defaultTool = tool; localStorage.defaultTool = tool;
} }
@@ -1,8 +1,8 @@
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx'; import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { EdgelessEraserDarkIcon, EdgelessEraserLightIcon } from './icons.js'; import { EdgelessEraserDarkIcon, EdgelessEraserLightIcon } from './icons.js';
export class EdgelessEraserToolButton extends EdgelessToolbarToolMixin( export class EdgelessEraserToolButton extends EdgelessToolbarToolMixin(
@@ -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 type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { repeat } from 'lit/directives/repeat.js'; import { repeat } from 'lit/directives/repeat.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { FrameConfig } from './config.js'; import { FrameConfig } from './config.js';
export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) { export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
@@ -66,8 +67,12 @@ export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
override type: GfxToolsFullOptionValue['type'] = 'frame'; override type: GfxToolsFullOptionValue['type'] = 'frame';
get frameManager() {
return this.edgeless.std.get(EdgelessFrameManagerIdentifier);
}
override render() { override render() {
const { edgeless } = this; const { gfx, frameManager } = this;
return html` return html`
<edgeless-slide-menu .showNext=${false}> <edgeless-slide-menu .showNext=${false}>
<div class="menu-content"> <div class="menu-content">
@@ -79,8 +84,8 @@ export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
(item, index) => html` (item, index) => html`
<div <div
@click=${() => { @click=${() => {
edgeless.gfx.tool.setTool('default'); gfx.tool.setTool('default');
edgeless.service.frame.createFrameOnViewportCenter(item.wh); frameManager.createFrameOnViewportCenter(item.wh);
}} }}
class="frame-add-button ${index}" class="frame-add-button ${index}"
data-name="${item.name}" data-name="${item.name}"
@@ -1,9 +1,8 @@
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx'; import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { FrameIcon } from '@blocksuite/icons/lit'; import { FrameIcon } from '@blocksuite/icons/lit';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
export class EdgelessFrameToolButton extends QuickToolMixin(LitElement) { export class EdgelessFrameToolButton extends QuickToolMixin(LitElement) {
static override styles = css` static override styles = css`
:host { :host {
@@ -1,10 +1,10 @@
import { LassoMode } from '@blocksuite/affine-shared/types'; import { LassoMode } from '@blocksuite/affine-shared/types';
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { WithDisposable } from '@blocksuite/global/lit'; import { WithDisposable } from '@blocksuite/global/lit';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { query, state } from 'lit/decorators.js'; import { query, state } from 'lit/decorators.js';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
import { LassoFreeHandIcon, LassoPolygonalIcon } from './icons.js'; import { LassoFreeHandIcon, LassoPolygonalIcon } from './icons.js';
export class EdgelessLassoToolButton extends QuickToolMixin( export class EdgelessLassoToolButton extends QuickToolMixin(
@@ -54,7 +54,7 @@ export class EdgelessLassoToolButton extends QuickToolMixin(
this.disposables.add( this.disposables.add(
effect(() => { effect(() => {
const tool = this.edgeless.gfx.tool.currentToolOption$.value; const tool = this.gfx.tool.currentToolOption$.value;
if (tool?.type === 'lasso') { if (tool?.type === 'lasso') {
const { mode } = tool; const { mode } = tool;
@@ -1,10 +1,9 @@
import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark'; import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark';
import { LinkIcon } from '@blocksuite/affine-components/icons'; import { LinkIcon } from '@blocksuite/affine-components/icons';
import { TelemetryProvider } from '@blocksuite/affine-shared/services'; import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { QuickToolMixin } from '../mixins/quick-tool.mixin.js';
export class EdgelessLinkToolButton extends QuickToolMixin(LitElement) { export class EdgelessLinkToolButton extends QuickToolMixin(LitElement) {
static override styles = css` static override styles = css`
.link-icon, .link-icon,
@@ -1,7 +1,10 @@
import { addAttachments } from '@blocksuite/affine-block-attachment'; import { addAttachments } from '@blocksuite/affine-block-attachment';
import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text'; import { insertEdgelessTextCommand } from '@blocksuite/affine-block-edgeless-text';
import { addImages } from '@blocksuite/affine-block-image'; 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 { mountTextElementEditor } from '@blocksuite/affine-gfx-text';
import { import {
MAX_IMAGE_WIDTH, MAX_IMAGE_WIDTH,
@@ -13,13 +16,11 @@ import {
TelemetryProvider, TelemetryProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { openFileOrFiles } from '@blocksuite/affine-shared/utils'; import { openFileOrFiles } from '@blocksuite/affine-shared/utils';
import type { BlockComponent } from '@blocksuite/block-std';
import { Bound } from '@blocksuite/global/gfx'; import { Bound } from '@blocksuite/global/gfx';
import type { TemplateResult } from 'lit'; import type { TemplateResult } from 'lit';
import * as Y from 'yjs'; 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 ConfigProperty = 'x' | 'y' | 'r' | 's' | 'z' | 'o';
export type ConfigState = 'default' | 'active' | 'hover' | 'next'; export type ConfigState = 'default' | 'active' | 'hover' | 'next';
export type ConfigStyle = Partial<Record<ConfigProperty, number | string>>; export type ConfigStyle = Partial<Record<ConfigProperty, number | string>>;
@@ -30,11 +31,7 @@ export type DraggableTool = {
icon: TemplateResult; icon: TemplateResult;
config: ToolConfig; config: ToolConfig;
standardWidth?: number; standardWidth?: number;
render: ( render: (bound: Bound, edgeless: BlockComponent) => Promise<string | null>;
bound: Bound,
edgelessService: EdgelessRootService,
edgeless: EdgelessRootBlockComponent
) => Promise<string | null>;
}; };
const unitMap = { x: 'px', y: 'px', r: 'deg', s: '', z: '', o: '' }; const unitMap = { x: 'px', y: 'px', r: 'deg', s: '', z: '', o: '' };
@@ -59,7 +56,7 @@ export const mediaConfig: ToolConfig = {
export const getMindmapRender = export const getMindmapRender =
(mindmapStyle: MindmapStyle): DraggableTool['render'] => (mindmapStyle: MindmapStyle): DraggableTool['render'] =>
async (bound, edgelessService) => { async (bound, edgeless) => {
const [x, y, _, h] = bound.toXYWH(); const [x, y, _, h] = bound.toXYWH();
const rootW = 145; 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, style: mindmapStyle,
children: root, children: root,
}) as string; }) as string;
edgelessService.std edgeless.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
.getOptional(TelemetryProvider) control: 'toolbar:dnd', // for now we use toolbar:dnd for all mindmap creation here
?.track('CanvasElementAdded', { page: 'whiteboard editor',
control: 'toolbar:dnd', // for now we use toolbar:dnd for all mindmap creation here module: 'toolbar',
page: 'whiteboard editor', segment: 'toolbar',
module: 'toolbar', type: 'mindmap',
segment: 'toolbar', });
type: 'mindmap',
});
return mindmapId; return mindmapId;
}; };
export const textRender: DraggableTool['render'] = async ( export const textRender: DraggableTool['render'] = async (bound, edgeless) => {
bound,
service,
edgeless
) => {
const vCenter = bound.y + bound.h / 2; const vCenter = bound.y + bound.h / 2;
const w = 100; const w = 100;
const h = 32; const h = 32;
@@ -135,13 +127,14 @@ export const textRender: DraggableTool['render'] = async (
); );
id = textId!; id = textId!;
} else { } 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(), xywh: new Bound(bound.x, vCenter - h / 2, w, h).serialize(),
text: new Y.Text(), text: new Y.Text(),
}) as string; }) as string;
edgeless.doc.captureSync(); edgeless.doc.captureSync();
const textElement = edgeless.service.crud.getElementById(id); const textElement = crud.getElementById(id);
if (!(textElement instanceof TextElementModel)) { if (!(textElement instanceof TextElementModel)) {
console.error('Cannot mount text editor on a non-text element'); console.error('Cannot mount text editor on a non-text element');
return null; return null;
@@ -149,7 +142,7 @@ export const textRender: DraggableTool['render'] = async (
mountTextElementEditor(textElement, edgeless); mountTextElementEditor(textElement, edgeless);
} }
service.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', { edgeless.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
control: 'toolbar:dnd', control: 'toolbar:dnd',
page: 'whiteboard editor', page: 'whiteboard editor',
module: 'toolbar', module: 'toolbar',
@@ -160,11 +153,7 @@ export const textRender: DraggableTool['render'] = async (
return id; return id;
}; };
export const mediaRender: DraggableTool['render'] = async ( export const mediaRender: DraggableTool['render'] = async (bound, edgeless) => {
bound,
_,
edgeless
) => {
let file: File | null = null; let file: File | null = null;
try { try {
file = await openFileOrFiles(); file = await openFileOrFiles();
@@ -5,6 +5,7 @@ import {
FeatureFlagService, FeatureFlagService,
TelemetryProvider, TelemetryProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { BlockStdScope } from '@blocksuite/block-std'; import type { BlockStdScope } from '@blocksuite/block-std';
import { modelContext, stdContext } from '@blocksuite/block-std'; import { modelContext, stdContext } from '@blocksuite/block-std';
import { ErrorCode } from '@blocksuite/global/exceptions'; import { ErrorCode } from '@blocksuite/global/exceptions';
@@ -19,7 +20,6 @@ import { repeat } from 'lit/directives/repeat.js';
import type { EdgelessRootBlockComponent } from '../../../index.js'; import type { EdgelessRootBlockComponent } from '../../../index.js';
import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.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 { getMindMaps, type ToolbarMindmapItem } from './assets.js';
import { mediaRender, textRender } from './basket-elements.js'; import { mediaRender, textRender } from './basket-elements.js';
import { importMindMapIcon, mindmapMenuMediaIcon, textIcon } from './icons.js'; import { importMindMapIcon, mindmapMenuMediaIcon, textIcon } from './icons.js';
@@ -220,7 +220,6 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin(
initDragController() { initDragController() {
if (this.draggableController || !this.edgeless) return; if (this.draggableController || !this.edgeless) return;
this.draggableController = new EdgelessDraggableElementController(this, { this.draggableController = new EdgelessDraggableElementController(this, {
service: this.edgeless.service,
edgeless: this.edgeless, edgeless: this.edgeless,
scopeElement: this, scopeElement: this,
clickToDrag: true, clickToDrag: true,
@@ -234,13 +233,13 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin(
onDrop: (element, bound) => { onDrop: (element, bound) => {
if ('render' in element.data) { if ('render' in element.data) {
element.data element.data
.render(bound, this.edgeless.service, this.edgeless) .render(bound, this.edgeless)
.then(id => { .then(id => {
if (!id) return; if (!id) return;
if (element.data.type === 'mindmap') { if (element.data.type === 'mindmap') {
this.onActiveStyleChange?.(element.data.style); this.onActiveStyleChange?.(element.data.style);
this.setEdgelessTool({ type: 'default' }); this.setEdgelessTool({ type: 'default' });
this.edgeless.gfx.selection.set({ this.gfx.selection.set({
elements: [id], elements: [id],
editing: false, editing: false,
}); });
@@ -8,6 +8,7 @@ import {
ThemeProvider, ThemeProvider,
ViewportElementProvider, ViewportElementProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx'; import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import type { Bound } from '@blocksuite/global/gfx'; import type { Bound } from '@blocksuite/global/gfx';
import { SignalWatcher } from '@blocksuite/global/lit'; 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 { styleMap } from 'lit/directives/style-map.js';
import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js'; import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { getMindMaps } from './assets.js'; import { getMindMaps } from './assets.js';
import { import {
type DraggableTool, type DraggableTool,
@@ -210,7 +210,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
this.tryDisposePopper(); this.tryDisposePopper();
this.setEdgelessTool({ type: 'default' }); this.setEdgelessTool({ type: 'default' });
this.edgeless.gfx.selection.set({ this.gfx.selection.set({
elements: [element.tree.id], elements: [element.tree.id],
editing: false, editing: false,
}); });
@@ -223,7 +223,6 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
if (!this.edgeless || !this.toolbarContainer) return; if (!this.edgeless || !this.toolbarContainer) return;
if (this.draggableController) return; if (this.draggableController) return;
this.draggableController = new EdgelessDraggableElementController(this, { this.draggableController = new EdgelessDraggableElementController(this, {
service: this.edgeless.service,
edgeless: this.edgeless, edgeless: this.edgeless,
scopeElement: this.toolbarContainer, scopeElement: this.toolbarContainer,
standardWidth: 100, standardWidth: 100,
@@ -265,13 +264,13 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
}, },
onDrop: (el, bound) => { onDrop: (el, bound) => {
el.data el.data
.render(bound, this.edgeless.service, this.edgeless) .render(bound, this.edgeless)
.then(id => { .then(id => {
if (!id) return; if (!id) return;
this.readyToDrop = false; this.readyToDrop = false;
if (el.data.name === 'mindmap') { if (el.data.name === 'mindmap') {
this.setEdgelessTool({ type: 'default' }); this.setEdgelessTool({ type: 'default' });
this.edgeless.gfx.selection.set({ this.gfx.selection.set({
elements: [id], elements: [id],
editing: false, editing: false,
}); });
@@ -286,9 +285,10 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
this.edgeless.bindHotKey( this.edgeless.bindHotKey(
{ {
m: () => { m: () => {
const service = this.edgeless.service; const gfx = this.gfx;
if (service.locked) return; const locked = gfx.viewport.locked;
if (service.selection.editing) return; if (locked) return;
if (gfx.selection.editing) return;
if (this.readyToDrop) { if (this.readyToDrop) {
// change the style // change the style
@@ -309,7 +309,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
} }
this.setEdgelessTool({ type: 'empty' }); this.setEdgelessTool({ type: 'empty' });
const icon = this.mindmapElement; 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 { viewport } = this.edgeless.std.get(ViewportElementProvider);
const { left, top } = viewport; const { left, top } = viewport;
const clientPos = { x: x + left, y: y + top }; const clientPos = { x: x + left, y: y + top };
@@ -9,6 +9,7 @@ import {
getImageFilesFromLocal, getImageFilesFromLocal,
openFileOrFiles, openFileOrFiles,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx'; import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit'; 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 { repeat } from 'lit/directives/repeat.js';
import type { NoteToolOption } from '../../../gfx-tool/note-tool.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'; import { NOTE_MENU_ITEMS } from './note-menu-config.js';
export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) { export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
@@ -60,8 +60,8 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
maxWidth: MAX_IMAGE_WIDTH, maxWidth: MAX_IMAGE_WIDTH,
}); });
this._imageLoading = false; this._imageLoading = false;
this.edgeless.gfx.tool.setTool('default'); this.gfx.tool.setTool('default');
this.edgeless.gfx.selection.set({ elements: ids }); this.gfx.selection.set({ elements: ids });
} }
private _onHandleLinkButtonClick() { private _onHandleLinkButtonClick() {
@@ -93,7 +93,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
override firstUpdated() { override firstUpdated() {
this.disposables.add( this.disposables.add(
effect(() => { effect(() => {
const tool = this.edgeless.gfx.tool.currentToolOption$.value; const tool = this.gfx.tool.currentToolOption$.value;
if (tool?.type !== 'affine:note') return; if (tool?.type !== 'affine:note') return;
this.childFlavour = tool.childFlavour; this.childFlavour = tool.childFlavour;
@@ -140,7 +140,7 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
const file = await openFileOrFiles(); const file = await openFileOrFiles();
if (!file) return; if (!file) return;
await addAttachments(this.edgeless.std, [file]); await addAttachments(this.edgeless.std, [file]);
this.edgeless.gfx.tool.setTool('default'); this.gfx.tool.setTool('default');
this.edgeless.std this.edgeless.std
.getOptional(TelemetryProvider) .getOptional(TelemetryProvider)
?.track('CanvasElementAdded', { ?.track('CanvasElementAdded', {
@@ -7,13 +7,13 @@ import {
EditPropsStore, EditPropsStore,
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit'; import { SignalWatcher } from '@blocksuite/global/lit';
import { computed } from '@preact/signals-core'; import { computed } from '@preact/signals-core';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { state } from 'lit/decorators.js'; import { state } from 'lit/decorators.js';
import type { NoteToolOption } from '../../../gfx-tool/note-tool.js'; import type { NoteToolOption } from '../../../gfx-tool/note-tool.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { toShapeNotToAdapt } from './icon.js'; import { toShapeNotToAdapt } from './icon.js';
export class EdgelessNoteSeniorButton extends EdgelessToolbarToolMixin( export class EdgelessNoteSeniorButton extends EdgelessToolbarToolMixin(
@@ -1,6 +1,7 @@
import { import {
createPopper, createPopper,
type MenuPopper, type MenuPopper,
QuickToolMixin,
} from '@blocksuite/affine-widget-edgeless-toolbar'; } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx'; import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { PageIcon } from '@blocksuite/icons/lit'; import { PageIcon } from '@blocksuite/icons/lit';
@@ -9,7 +10,6 @@ import { css, html, LitElement } from 'lit';
import { state } from 'lit/decorators.js'; import { state } from 'lit/decorators.js';
import type { NoteToolOption } from '../../../gfx-tool/note-tool.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'; import type { EdgelessNoteMenu } from './note-menu.js';
export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) { export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
@@ -35,7 +35,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
this._disposeMenu(); this._disposeMenu();
this.requestUpdate(); this.requestUpdate();
} else { } else {
this.edgeless.gfx.tool.setTool('affine:note', { this.gfx.tool.setTool('affine:note', {
childFlavour: this.childFlavour, childFlavour: this.childFlavour,
childType: this.childType, childType: this.childType,
tip: this.tip, tip: this.tip,
@@ -59,7 +59,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
Object.assign(this, { [key]: props[key] }); Object.assign(this, { [key]: props[key] });
} }
}); });
this.edgeless.gfx.tool.setTool('affine:note', { this.gfx.tool.setTool('affine:note', {
childFlavour: this.childFlavour, childFlavour: this.childFlavour,
childType: this.childType, childType: this.childType,
tip: this.tip, tip: this.tip,
@@ -72,7 +72,7 @@ export class EdgelessNoteToolButton extends QuickToolMixin(LitElement) {
super.connectedCallback(); super.connectedCallback();
this._disposables.add( this._disposables.add(
effect(() => { effect(() => {
const value = this.edgeless.gfx.tool.currentToolName$.value; const value = this.gfx.tool.currentToolName$.value;
if (value !== 'affine:note') { if (value !== 'affine:note') {
this._disposeMenu(); this._disposeMenu();
} }
@@ -1,10 +1,11 @@
import {
EdgelessToolbarToolMixin,
QuickToolMixin,
} from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx'; import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { PresentationIcon } from '@blocksuite/icons/lit'; import { PresentationIcon } from '@blocksuite/icons/lit';
import { css, html, LitElement } from '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( export class EdgelessPresentButton extends QuickToolMixin(
EdgelessToolbarToolMixin(LitElement) EdgelessToolbarToolMixin(LitElement)
) { ) {
@@ -1,10 +1,16 @@
import { import {
EdgelessFrameManagerIdentifier,
isFrameBlock, isFrameBlock,
type NavigatorMode, type NavigatorMode,
} from '@blocksuite/affine-block-frame'; } from '@blocksuite/affine-block-frame';
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
import { toast } from '@blocksuite/affine-components/toast'; import { toast } from '@blocksuite/affine-components/toast';
import type { FrameBlockModel } from '@blocksuite/affine-model'; 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 type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { Bound, clamp } from '@blocksuite/global/gfx'; import { Bound, clamp } from '@blocksuite/global/gfx';
import { SignalWatcher } from '@blocksuite/global/lit'; 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 type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
import { launchIntoFullscreen } from '../utils.js'; import { launchIntoFullscreen } from '../utils.js';
import { EdgelessToolbarToolMixin } from './mixins/tool.mixin.js';
export class PresentationToolbar extends EdgelessToolbarToolMixin( export class PresentationToolbar extends EdgelessToolbarToolMixin(
SignalWatcher(LitElement) SignalWatcher(LitElement)
@@ -125,7 +130,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
} }
private get _frames(): FrameBlockModel[] { private get _frames(): FrameBlockModel[] {
return this.edgeless.service.frames; return this.edgeless.std.get(EdgelessFrameManagerIdentifier).frames;
} }
get dense() { get dense() {
@@ -136,6 +141,10 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
return this.edgeless.host; return this.edgeless.host;
} }
get slots() {
return this.edgeless.std.get(EdgelessLegacySlotIdentifier);
}
constructor(edgeless: EdgelessRootBlockComponent) { constructor(edgeless: EdgelessRootBlockComponent) {
super(); super();
this.edgeless = edgeless; this.edgeless = edgeless;
@@ -176,7 +185,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
private _moveToCurrentFrame() { private _moveToCurrentFrame() {
const current = this._currentFrameIndex; const current = this._currentFrameIndex;
const viewport = this.edgeless.service.viewport; const viewport = this.gfx.viewport;
const frame = this._frames[current]; const frame = this._frames[current];
if (frame) { if (frame) {
@@ -197,7 +206,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
} }
viewport.setViewportByBound(bound, [0, 0, 0, 0], false); viewport.setViewportByBound(bound, [0, 0, 0, 0], false);
this.edgeless.slots.navigatorFrameChanged.next( this.slots.navigatorFrameChanged.next(
this._frames[this._currentFrameIndex] this._frames[this._currentFrameIndex]
); );
} }
@@ -235,7 +244,10 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
document.exitFullscreen().catch(console.error); document.exitFullscreen().catch(console.error);
this._fullScreenMode = false; this._fullScreenMode = false;
} else { } else {
launchIntoFullscreen(this.edgeless.viewportElement); const { viewportElement } = this.edgeless.std.get(
ViewportElementProvider
);
launchIntoFullscreen(viewportElement);
this._fullScreenMode = true; this._fullScreenMode = true;
} }
} }
@@ -243,19 +255,19 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
override connectedCallback(): void { override connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
const { _disposables, edgeless } = this; const { _disposables } = this;
_disposables.add( _disposables.add(
effect(() => { 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') { if (currentTool?.type === 'frameNavigator') {
this._cachedIndex = this._currentFrameIndex; this._cachedIndex = this._currentFrameIndex;
this._navigatorMode = currentTool.mode ?? this._navigatorMode; this._navigatorMode = currentTool.mode ?? this._navigatorMode;
if (isFrameBlock(edgeless.service.selection.selectedElements[0])) { if (isFrameBlock(selection.selectedElements[0])) {
this._cachedIndex = this._frames.findIndex( this._cachedIndex = this._frames.findIndex(
frame => frame => frame.id === selection.selectedElements[0].id
frame.id === edgeless.service.selection.selectedElements[0].id
); );
} }
if (this._frames.length === 0) if (this._frames.length === 0)
@@ -273,12 +285,12 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
} }
override firstUpdated() { override firstUpdated() {
const { _disposables, edgeless } = this; const { _disposables } = this;
this._bindHotKey(); this._bindHotKey();
_disposables.add( _disposables.add(
edgeless.slots.navigatorSettingUpdated.subscribe(({ fillScreen }) => { this.slots.navigatorSettingUpdated.subscribe(({ fillScreen }) => {
if (fillScreen !== undefined) { if (fillScreen !== undefined) {
this._navigatorMode = fillScreen ? 'fill' : 'fit'; this._navigatorMode = fillScreen ? 'fill' : 'fit';
} }
@@ -307,7 +319,7 @@ export class PresentationToolbar extends EdgelessToolbarToolMixin(
} }
setTimeout(() => this._moveToCurrentFrame(), 400); setTimeout(() => this._moveToCurrentFrame(), 400);
this.edgeless.slots.fullScreenToggled.next(); this.slots.fullScreenToggled.next();
}); });
this._navigatorMode = this._navigatorMode =
@@ -19,6 +19,7 @@ import {
ThemeProvider, ThemeProvider,
ViewportElementProvider, ViewportElementProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit'; import { SignalWatcher } from '@blocksuite/global/lit';
import { css, html, LitElement, nothing } from 'lit'; import { css, html, LitElement, nothing } from 'lit';
import { property, query, state } from 'lit/decorators.js'; 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 { styleMap } from 'lit/directives/style-map.js';
import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js'; import { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import type { DraggableShape } from './utils.js'; import type { DraggableShape } from './utils.js';
import { buildVariablesObject } from './utils.js'; import { buildVariablesObject } from './utils.js';
@@ -152,7 +152,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
} }
private _setShapeOverlayLock(lock: boolean) { private _setShapeOverlayLock(lock: boolean) {
const controller = this.edgeless.gfx.tool.currentTool$.peek(); const controller = this.gfx.tool.currentTool$.peek();
if (controller instanceof ShapeTool) { if (controller instanceof ShapeTool) {
controller.setDisableOverlay(lock); controller.setDisableOverlay(lock);
} }
@@ -162,7 +162,6 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
if (!this.edgeless || !this.toolbarContainer) return; if (!this.edgeless || !this.toolbarContainer) return;
if (this.draggableController) return; if (this.draggableController) return;
this.draggableController = new EdgelessDraggableElementController(this, { this.draggableController = new EdgelessDraggableElementController(this, {
service: this.edgeless.service,
edgeless: this.edgeless, edgeless: this.edgeless,
scopeElement: this.toolbarContainer, scopeElement: this.toolbarContainer,
standardWidth: 100, standardWidth: 100,
@@ -176,7 +175,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
type: 'shape', type: 'shape',
shapeName, shapeName,
}); });
const controller = this.edgeless.gfx.tool.currentTool$.peek(); const controller = this.gfx.tool.currentTool$.peek();
if (controller instanceof ShapeTool) { if (controller instanceof ShapeTool) {
controller.clearOverlay(); controller.clearOverlay();
} }
@@ -209,8 +208,8 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
this._setShapeOverlayLock(false); this._setShapeOverlayLock(false);
this.readyToDrop = false; this.readyToDrop = false;
this.edgeless.gfx.tool.setTool('default'); this.gfx.tool.setTool('default');
this.edgeless.gfx.selection.set({ this.gfx.selection.set({
elements: [id], elements: [id],
editing: false, 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 // `page.keyboard.press('Shift+s')` in playwright will also trigger this 's' key event
if (ctx.get('keyboardState').raw.shiftKey) return; if (ctx.get('keyboardState').raw.shiftKey) return;
const service = this.edgeless.service; const locked = this.gfx.viewport.locked;
if (service.locked || service.selection.editing) return; const selection = this.gfx.selection;
if (locked || selection.editing) return;
if (this.readyToDrop) { if (this.readyToDrop) {
const activeIndex = shapes.findIndex( const activeIndex = shapes.findIndex(
@@ -257,7 +257,7 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin(
console.error('Edgeless toolbar Shape element not found'); console.error('Edgeless toolbar Shape element not found');
return; 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 { viewport } = this.edgeless.std.get(ViewportElementProvider);
const { left, top } = viewport; const { left, top } = viewport;
const clientPos = { x: x + left, y: y + top }; const clientPos = { x: x + left, y: y + top };
@@ -1,9 +1,9 @@
import { ShapeTool } from '@blocksuite/affine-gfx-shape'; import { ShapeTool } from '@blocksuite/affine-gfx-shape';
import { type ShapeName, ShapeType } from '@blocksuite/affine-model'; import { type ShapeName, ShapeType } from '@blocksuite/affine-model';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import { SignalWatcher } from '@blocksuite/global/lit'; import { SignalWatcher } from '@blocksuite/global/lit';
import { css, html, LitElement } from 'lit'; import { css, html, LitElement } from 'lit';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import type { DraggableShape } from './utils.js'; import type { DraggableShape } from './utils.js';
export class EdgelessShapeToolButton extends EdgelessToolbarToolMixin( export class EdgelessShapeToolButton extends EdgelessToolbarToolMixin(
@@ -55,7 +55,7 @@ export class EdgelessShapeToolButton extends EdgelessToolbarToolMixin(
} }
private _updateOverlay() { private _updateOverlay() {
const controller = this.edgeless.gfx.tool.currentTool$.peek(); const controller = this.gfx.tool.currentTool$.peek();
if (controller instanceof ShapeTool) { if (controller instanceof ShapeTool) {
controller.createOverlay(); controller.createOverlay();
} }
@@ -10,6 +10,8 @@ import {
requestConnectedFrame, requestConnectedFrame,
stopPropagation, stopPropagation,
} from '@blocksuite/affine-shared/utils'; } 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 type { Bound } from '@blocksuite/global/gfx';
import { WithDisposable } from '@blocksuite/global/lit'; import { WithDisposable } from '@blocksuite/global/lit';
import { baseTheme } from '@toeverything/theme'; 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 { styleMap } from 'lit/directives/style-map.js';
import { unsafeSVG } from 'lit/directives/unsafe-svg.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 { EdgelessDraggableElementController } from '../common/draggable/draggable-element.controller.js';
import { builtInTemplates } from './builtin-templates.js'; import { builtInTemplates } from './builtin-templates.js';
import { defaultPreview, Triangle } from './cards.js'; import { defaultPreview, Triangle } from './cards.js';
@@ -252,7 +254,6 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
private _initDragController() { private _initDragController() {
if (this.draggableController) return; if (this.draggableController) return;
this.draggableController = new EdgelessDraggableElementController(this, { this.draggableController = new EdgelessDraggableElementController(this, {
service: this.edgeless.service,
edgeless: this.edgeless, edgeless: this.edgeless,
clickToDrag: true, clickToDrag: true,
standardWidth: 560, 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) { private async _insertTemplate(template: Template, bound: Bound) {
this._loadingTemplate = template; this._loadingTemplate = template;
@@ -282,11 +291,7 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
x: bound.x + bound.w / 2, x: bound.x + bound.w / 2,
y: bound.y + bound.h / 2, y: bound.y + bound.h / 2,
}; };
const templateJob = this.edgeless.service.createTemplateJob( const templateJob = this.service.createTemplateJob(template.type, center);
template.type,
center
);
const service = this.edgeless.service;
try { try {
const { assets } = template; const { assets } = template;
@@ -304,8 +309,8 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
const insertedBound = await templateJob.insertTemplate(template.content); const insertedBound = await templateJob.insertTemplate(template.content);
if (insertedBound && template.type === 'template') { if (insertedBound && template.type === 'template') {
const padding = 20 / service.viewport.zoom; const padding = 20 / this.gfx.viewport.zoom;
service.viewport.setViewportByBound( this.gfx.viewport.setViewportByBound(
insertedBound, insertedBound,
[padding, padding, padding, padding], [padding, padding, padding, padding],
true true
@@ -313,7 +318,7 @@ export class EdgelessTemplatePanel extends WithDisposable(LitElement) {
} }
} finally { } finally {
this._loadingTemplate = null; 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[] = []; private accessor _templates: Template[] = [];
@property({ attribute: false }) @property({ attribute: false })
accessor edgeless!: EdgelessRootBlockComponent; accessor edgeless!: BlockComponent;
@state() @state()
accessor isDragging = false; accessor isDragging = false;
@@ -1,5 +1,6 @@
import { ArrowDownSmallIcon } from '@blocksuite/affine-components/icons'; import { ArrowDownSmallIcon } from '@blocksuite/affine-components/icons';
import { once } from '@blocksuite/affine-shared/utils'; import { once } from '@blocksuite/affine-shared/utils';
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx'; import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { import {
arrow, arrow,
@@ -13,7 +14,6 @@ import { state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
import { repeat } from 'lit/directives/repeat.js'; import { repeat } from 'lit/directives/repeat.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
import { TemplateCard1, TemplateCard2, TemplateCard3 } from './cards.js'; import { TemplateCard1, TemplateCard2, TemplateCard3 } from './cards.js';
import type { EdgelessTemplatePanel } from './template-panel.js'; import type { EdgelessTemplatePanel } from './template-panel.js';
@@ -1,13 +1,12 @@
import { DefaultTheme } from '@blocksuite/affine-model'; import { DefaultTheme } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import type { ColorEvent } from '@blocksuite/affine-shared/utils'; 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 type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { computed } from '@preact/signals-core'; import { computed } from '@preact/signals-core';
import { css, html, LitElement, nothing } from 'lit'; import { css, html, LitElement, nothing } from 'lit';
import { property } from 'lit/decorators.js'; import { property } from 'lit/decorators.js';
import { EdgelessToolbarToolMixin } from '../mixins/tool.mixin.js';
export class EdgelessTextMenu extends EdgelessToolbarToolMixin(LitElement) { export class EdgelessTextMenu extends EdgelessToolbarToolMixin(LitElement) {
static override styles = css` static override styles = css`
:host { :host {
@@ -2,3 +2,4 @@ export * from './context';
export * from './create-popper'; export * from './create-popper';
export * from './edgeless-toolbar'; export * from './edgeless-toolbar';
export * from './extension'; export * from './extension';
export * from './mixins';
@@ -0,0 +1,3 @@
export * from './quick-tool.mixin';
export * from './tool.mixin';
export * from './toolbar-button-with-menu.mixin';
@@ -1,17 +1,11 @@
import type { ColorScheme } from '@blocksuite/affine-model'; import type { ColorScheme } from '@blocksuite/affine-model';
import type { BlockComponent } from '@blocksuite/block-std';
import { import {
createPopper, type GfxController,
edgelessToolbarContext, GfxControllerIdentifier,
type EdgelessToolbarSlots, type GfxToolsFullOption,
edgelessToolbarSlotsContext, type GfxToolsFullOptionValue,
edgelessToolbarThemeContext, type ToolController,
type EdgelessToolbarWidget,
type MenuPopper,
} from '@blocksuite/affine-widget-edgeless-toolbar';
import type {
GfxToolsFullOption,
GfxToolsFullOptionValue,
ToolController,
} from '@blocksuite/block-std/gfx'; } from '@blocksuite/block-std/gfx';
import { import {
// oxlint-disable-next-line no-unused-vars // oxlint-disable-next-line no-unused-vars
@@ -25,7 +19,14 @@ import { cssVar } from '@toeverything/theme';
import type { LitElement } from 'lit'; import type { LitElement } from 'lit';
import { property, state } from 'lit/decorators.js'; 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]; type ValueOf<T> = T[keyof T];
@@ -34,7 +35,7 @@ export declare abstract class EdgelessToolbarToolClass extends DisposableClass {
createPopper: typeof createPopper; createPopper: typeof createPopper;
edgeless: EdgelessRootBlockComponent; edgeless: BlockComponent;
edgelessTool: GfxToolsFullOptionValue; edgelessTool: GfxToolsFullOptionValue;
@@ -44,6 +45,8 @@ export declare abstract class EdgelessToolbarToolClass extends DisposableClass {
setEdgelessTool: ToolController['setTool']; setEdgelessTool: ToolController['setTool'];
gfx: GfxController;
theme: ColorScheme; theme: ColorScheme;
toolbarContainer: HTMLElement | null; toolbarContainer: HTMLElement | null;
@@ -74,18 +77,24 @@ export const EdgelessToolbarToolMixin = <T extends Constructor<LitElement>>(
get active() { get active() {
const { type } = this; const { type } = this;
// @ts-expect-error FIXME: we need to fix the type of edgelessTool
const activeType = this.edgelessTool?.type; const activeType = this.edgelessTool?.type;
return activeType return activeType
? Array.isArray(type) ? Array.isArray(type)
? type.includes(activeType) ? // @ts-expect-error FIXME: we need to fix the type of edgelessTool
type.includes(activeType)
: activeType === type : activeType === type
: false; : false;
} }
get gfx() {
return this.edgeless.std.get(GfxControllerIdentifier);
}
get setEdgelessTool() { get setEdgelessTool() {
return (...args: Parameters<ToolController['setTool']>) => { return (...args: Parameters<ToolController['setTool']>) => {
this.edgeless.gfx.tool.setTool( this.gfx.tool.setTool(
// @ts-expect-error FIXME: ts error // @ts-expect-error FIXME: ts error
...args ...args
); );
@@ -100,7 +109,7 @@ export const EdgelessToolbarToolMixin = <T extends Constructor<LitElement>>(
} }
private _updateActiveEdgelessTool() { private _updateActiveEdgelessTool() {
this.edgelessTool = this.edgeless.gfx.tool.currentToolOption$.value; this.edgelessTool = this.gfx.tool.currentToolOption$.value;
this._applyActiveStyle(); this._applyActiveStyle();
} }
@@ -150,7 +159,7 @@ export const EdgelessToolbarToolMixin = <T extends Constructor<LitElement>>(
} }
@property({ attribute: false }) @property({ attribute: false })
accessor edgeless!: EdgelessRootBlockComponent; accessor edgeless!: BlockComponent;
@state() @state()
accessor edgelessTool!: ValueOf<GfxToolsFullOption> | null; accessor edgelessTool!: ValueOf<GfxToolsFullOption> | null;