refactor(editor): query methods in edgeless api (#9407)

This commit is contained in:
Saul-Mirone
2024-12-28 07:48:41 +00:00
parent dc92d78895
commit 1e4b1807be
35 changed files with 296 additions and 275 deletions
@@ -137,4 +137,26 @@ export class EdgelessCRUDExtension extends Extension {
this.std.doc.updateBlock(block, props); this.std.doc.updateBlock(block, props);
} }
}; };
getElementById(id: string): BlockSuite.EdgelessModel | null {
const surface = this._surface;
if (!surface) {
return null;
}
const el =
surface.getElementById(id) ??
(this.std.doc.getBlockById(
id
) as BlockSuite.EdgelessBlockModelType | null);
return el;
}
getElementsByType<K extends keyof BlockSuite.SurfaceElementModelMap>(
type: K
): BlockSuite.SurfaceElementModelMap[K][] {
if (!this._surface) {
return [];
}
return this._surface.getElementsByType(type);
}
} }
@@ -0,0 +1,79 @@
import { FrameBlockModel, GroupElementModel } from '@blocksuite/affine-model';
import type { GfxBlockElementModel } from '@blocksuite/block-std/gfx';
import {
deserializeXYWH,
getQuadBoundWithRotation,
} from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store';
export function getSelectedRect(selected: BlockSuite.EdgelessModel[]): DOMRect {
if (selected.length === 0) {
return new DOMRect();
}
const lockedElementsByFrame = selected
.map(selectable => {
if (selectable instanceof FrameBlockModel && selectable.isLocked()) {
return selectable.descendantElements;
}
return [];
})
.flat();
selected = [...new Set([...selected, ...lockedElementsByFrame])];
if (selected.length === 1) {
const [x, y, w, h] = deserializeXYWH(selected[0].xywh);
return new DOMRect(x, y, w, h);
}
return getElementsWithoutGroup(selected).reduce(
(bounds, selectable, index) => {
const rotate = isTopLevelBlock(selectable) ? 0 : selectable.rotate;
const [x, y, w, h] = deserializeXYWH(selectable.xywh);
let { left, top, right, bottom } = getQuadBoundWithRotation({
x,
y,
w,
h,
rotate,
});
if (index !== 0) {
left = Math.min(left, bounds.left);
top = Math.min(top, bounds.top);
right = Math.max(right, bounds.right);
bottom = Math.max(bottom, bounds.bottom);
}
bounds.x = left;
bounds.y = top;
bounds.width = right - left;
bounds.height = bottom - top;
return bounds;
},
new DOMRect()
);
}
export function getElementsWithoutGroup(elements: BlockSuite.EdgelessModel[]) {
const set = new Set<BlockSuite.EdgelessModel>();
elements.forEach(element => {
if (element instanceof GroupElementModel) {
element.descendantElements
.filter(descendant => !(descendant instanceof GroupElementModel))
.forEach(descendant => set.add(descendant));
} else {
set.add(element);
}
});
return Array.from(set);
}
export function isTopLevelBlock(
selectable: BlockModel | BlockSuite.EdgelessModel | null
): selectable is GfxBlockElementModel {
return !!selectable && 'flavour' in selectable;
}
@@ -2,6 +2,7 @@ export * from './button-popper.js';
export * from './collapsed/index.js'; export * from './collapsed/index.js';
export * from './dnd/index.js'; export * from './dnd/index.js';
export * from './dom/index.js'; export * from './dom/index.js';
export * from './edgeless.js';
export * from './event.js'; export * from './event.js';
export * from './file/index.js'; export * from './file/index.js';
export * from './insert.js'; export * from './insert.js';
@@ -26,6 +26,7 @@ import {
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { import {
isInsidePageEditor, isInsidePageEditor,
isTopLevelBlock,
isUrlInClipboard, isUrlInClipboard,
matchFlavours, matchFlavours,
referenceToNode, referenceToNode,
@@ -83,7 +84,6 @@ import {
isAttachmentBlock, isAttachmentBlock,
isCanvasElementWithText, isCanvasElementWithText,
isImageBlock, isImageBlock,
isTopLevelBlock,
} from '../utils/query.js'; } from '../utils/query.js';
const BLOCKSUITE_SURFACE = 'blocksuite/surface'; const BLOCKSUITE_SURFACE = 'blocksuite/surface';
@@ -595,9 +595,7 @@ export class EdgelessClipboardController extends PageClipboard {
segment: 'toolbar', segment: 'toolbar',
type: clipboardData.type as string, type: clipboardData.type as string,
}); });
const element = this.host.service.getElementById( const element = this.crud.getElementById(id) as BlockSuite.SurfaceModel;
id
) as BlockSuite.SurfaceModel;
assertExists(element); assertExists(element);
return element; return element;
} }
@@ -167,7 +167,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
surfaceBlockModel surfaceBlockModel
); );
edgeless.doc.captureSync(); edgeless.doc.captureSync();
const frame = service.getElementById(id); const frame = this.crud.getElementById(id);
if (!frame) return; if (!frame) return;
this.connector.target = { this.connector.target = {
@@ -225,7 +225,6 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
const currentSource = this.currentSource; const currentSource = this.currentSource;
const { nextBound, position } = result; const { nextBound, position } = result;
const { service } = edgeless;
const id = createShapeElement(edgeless, currentSource, targetType); const id = createShapeElement(edgeless, currentSource, targetType);
if (!id) return; if (!id) return;
@@ -235,10 +234,10 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
}); });
mountShapeTextEditor( mountShapeTextEditor(
service.getElementById(id) as ShapeElementModel, this.crud.getElementById(id) as ShapeElementModel,
this.edgeless this.edgeless
); );
edgeless.service.selection.set({ this.gfx.selection.set({
elements: [id], elements: [id],
editing: true, editing: true,
}); });
@@ -250,7 +249,6 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
if (!target) return; if (!target) return;
const { xywh, position } = target; const { xywh, position } = target;
const bound = Bound.fromXYWH(xywh); const bound = Bound.fromXYWH(xywh);
const edgelessService = this.edgeless.service;
const textFlag = this.edgeless.doc.awarenessStore.getFlag( const textFlag = this.edgeless.doc.awarenessStore.getFlag(
'enable_edgeless_text' 'enable_edgeless_text'
@@ -262,7 +260,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
}); });
if (!textId) return; if (!textId) return;
const textElement = edgelessService.getElementById(textId); const textElement = this.crud.getElementById(textId);
if (!textElement) return; if (!textElement) return;
this.crud.updateElement(this.connector.id, { this.crud.updateElement(this.connector.id, {
@@ -272,7 +270,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
this.currentSource.group.addChild(textElement); this.currentSource.group.addChild(textElement);
} }
this.edgeless.service.selection.set({ this.gfx.selection.set({
elements: [textId], elements: [textId],
editing: false, editing: false,
}); });
@@ -289,7 +287,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
fontStyle: FontStyle.Normal, fontStyle: FontStyle.Normal,
}); });
if (!textId) return; if (!textId) return;
const textElement = edgelessService.getElementById(textId); const textElement = this.crud.getElementById(textId);
assertInstanceOf(textElement, TextElementModel); assertInstanceOf(textElement, TextElementModel);
this.crud.updateElement(this.connector.id, { this.crud.updateElement(this.connector.id, {
@@ -299,7 +297,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
this.currentSource.group.addChild(textElement); this.currentSource.group.addChild(textElement);
} }
this.edgeless.service.selection.set({ this.gfx.selection.set({
elements: [textId], elements: [textId],
editing: false, editing: false,
}); });
@@ -331,7 +329,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
} }
private _connectorExist() { private _connectorExist() {
return !!this.edgeless.service.getElementById(this.connector.id); return !!this.crud.getElementById(this.connector.id);
} }
private _generateTarget(connector: ConnectorElementModel) { private _generateTarget(connector: ConnectorElementModel) {
@@ -164,9 +164,8 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
private _autoCompleteOverlay!: AutoCompleteOverlay; private _autoCompleteOverlay!: AutoCompleteOverlay;
private readonly _onPointerDown = (e: PointerEvent, type: Direction) => { private readonly _onPointerDown = (e: PointerEvent, type: Direction) => {
const { service } = this.edgeless; const viewportRect = this.gfx.viewport.boundingClientRect;
const viewportRect = service.viewport.boundingClientRect; const start = this.gfx.viewport.toModelCoord(
const start = service.viewport.toModelCoord(
e.clientX - viewportRect.left, e.clientX - viewportRect.left,
e.clientY - viewportRect.top e.clientY - viewportRect.top
); );
@@ -176,7 +175,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
let connector: ConnectorElementModel | null; let connector: ConnectorElementModel | null;
this._disposables.addFromEvent(document, 'pointermove', e => { this._disposables.addFromEvent(document, 'pointermove', e => {
const point = service.viewport.toModelCoord( const point = this.gfx.viewport.toModelCoord(
e.clientX - viewportRect.left, e.clientX - viewportRect.left,
e.clientY - viewportRect.top e.clientY - viewportRect.top
); );
@@ -237,14 +236,17 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
return this.std.get(EdgelessCRUDIdentifier); return this.std.get(EdgelessCRUDIdentifier);
} }
get gfx() {
return this.std.get(GfxControllerIdentifier);
}
private _addConnector(source: Connection, target: Connection) { private _addConnector(source: Connection, target: Connection) {
const { edgeless } = this;
const id = this.crud.addElement(CanvasElementType.CONNECTOR, { const id = this.crud.addElement(CanvasElementType.CONNECTOR, {
source, source,
target, target,
}); });
if (!id) return null; if (!id) return null;
return edgeless.service.getElementById(id) as ConnectorElementModel; return this.crud.getElementById(id) as ConnectorElementModel;
} }
private _addMindmapNode(target: 'sibling' | 'child') { private _addMindmapNode(target: 'sibling' | 'child') {
@@ -274,7 +276,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
requestAnimationFrame(() => { requestAnimationFrame(() => {
mountShapeTextEditor( mountShapeTextEditor(
this.edgeless.service.getElementById(newNode) as ShapeElementModel, this.crud.getElementById(newNode) as ShapeElementModel,
this.edgeless this.edgeless
); );
}); });
@@ -375,7 +377,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
); );
mountShapeTextEditor( mountShapeTextEditor(
service.getElementById(id) as ShapeElementModel, this.crud.getElementById(id) as ShapeElementModel,
this.edgeless this.edgeless
); );
} else { } else {
@@ -403,12 +405,12 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
return service.getConnectors(element.id).reduce((prev, current) => { return service.getConnectors(element.id).reduce((prev, current) => {
if (current.target.id === element.id && current.source.id) { if (current.target.id === element.id && current.source.id) {
prev.push( prev.push(
service.getElementById(current.source.id) as ShapeElementModel this.crud.getElementById(current.source.id) as ShapeElementModel
); );
} }
if (current.source.id === element.id && current.target.id) { if (current.source.id === element.id && current.target.id) {
prev.push( prev.push(
service.getElementById(current.target.id) as ShapeElementModel this.crud.getElementById(current.target.id) as ShapeElementModel
); );
} }
@@ -467,9 +469,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
private _initOverlay() { private _initOverlay() {
const { surface } = this.edgeless; const { surface } = this.edgeless;
this._autoCompleteOverlay = new AutoCompleteOverlay( this._autoCompleteOverlay = new AutoCompleteOverlay(this.gfx);
this.std.get(GfxControllerIdentifier)
);
surface.renderer.addOverlay(this._autoCompleteOverlay); surface.renderer.addOverlay(this._autoCompleteOverlay);
} }
@@ -656,7 +656,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
override connectedCallback(): void { override connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
this._pathGenerator = new ConnectorPathGenerator({ this._pathGenerator = new ConnectorPathGenerator({
getElementById: id => this.edgeless.service.getElementById(id), getElementById: id => this.crud.getElementById(id),
}); });
this._initOverlay(); this._initOverlay();
} }
@@ -665,7 +665,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) {
const { _disposables, edgeless } = this; const { _disposables, edgeless } = this;
_disposables.add( _disposables.add(
this.edgeless.service.selection.slots.updated.on(() => { this.gfx.selection.slots.updated.on(() => {
this._autoCompleteOverlay.linePoints = []; this._autoCompleteOverlay.linePoints = [];
this._autoCompleteOverlay.renderShape = null; this._autoCompleteOverlay.renderShape = null;
}) })
@@ -277,17 +277,18 @@ export function createEdgelessElement(
) { ) {
let id; let id;
const { service } = edgeless; const { service } = edgeless;
const { crud } = service;
let element: GfxModel | null = null; let element: GfxModel | null = null;
if (isShape(current)) { if (isShape(current)) {
id = service.crud.addElement(current.type, { id = crud.addElement(current.type, {
...current.serialize(), ...current.serialize(),
text: new DocCollection.Y.Text(), text: new DocCollection.Y.Text(),
xywh: bound.serialize(), xywh: bound.serialize(),
}); });
if (!id) return null; if (!id) return null;
element = service.getElementById(id); element = crud.getElementById(id);
} else { } else {
const { doc } = edgeless; const { doc } = edgeless;
id = doc.addBlock( id = doc.addBlock(
@@ -335,14 +336,14 @@ export function createShapeElement(
current: ShapeElementModel | NoteBlockModel, current: ShapeElementModel | NoteBlockModel,
targetType: TARGET_SHAPE_TYPE targetType: TARGET_SHAPE_TYPE
) { ) {
const service = edgeless.service; const { crud } = edgeless.service;
const id = service.crud.addElement('shape', { const id = crud.addElement('shape', {
shapeType: getShapeType(targetType), shapeType: getShapeType(targetType),
radius: getShapeRadius(targetType), radius: getShapeRadius(targetType),
text: new DocCollection.Y.Text(), text: new DocCollection.Y.Text(),
}); });
if (!id) return null; if (!id) return null;
const element = service.getElementById(id); const element = crud.getElementById(id);
const group = current.group; const group = current.group;
if (group instanceof GroupElementModel && element) { if (group instanceof GroupElementModel && element) {
group.addChild(element); group.addChild(element);
@@ -31,6 +31,8 @@ import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts';
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme'; import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { import {
clamp, clamp,
getElementsWithoutGroup,
getSelectedRect,
requestThrottledConnectedFrame, requestThrottledConnectedFrame,
stopPropagation, stopPropagation,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
@@ -72,10 +74,8 @@ import {
AI_CHAT_BLOCK_MIN_HEIGHT, AI_CHAT_BLOCK_MIN_HEIGHT,
AI_CHAT_BLOCK_MIN_WIDTH, AI_CHAT_BLOCK_MIN_WIDTH,
} from '../../utils/consts.js'; } from '../../utils/consts.js';
import { getElementsWithoutGroup } from '../../utils/group.js';
import { import {
getSelectableBounds, getSelectableBounds,
getSelectedRect,
isAIChatBlock, isAIChatBlock,
isAttachmentBlock, isAttachmentBlock,
isBookmarkBlock, isBookmarkBlock,
@@ -7,6 +7,7 @@ import type { RichText } from '@blocksuite/affine-components/rich-text';
import type { ShapeElementModel } from '@blocksuite/affine-model'; import type { ShapeElementModel } from '@blocksuite/affine-model';
import { MindmapElementModel, TextResizing } from '@blocksuite/affine-model'; import { MindmapElementModel, TextResizing } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { getSelectedRect } from '@blocksuite/affine-shared/utils';
import { import {
RANGE_SYNC_EXCLUDE_ATTR, RANGE_SYNC_EXCLUDE_ATTR,
ShadowlessElement, ShadowlessElement,
@@ -23,7 +24,6 @@ import { property, query } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js'; import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
import { getSelectedRect } from '../../utils/query.js';
const { toRadian } = CommonUtils; const { toRadian } = CommonUtils;
@@ -6,6 +6,7 @@ import {
import type { RichText } from '@blocksuite/affine-components/rich-text'; import type { RichText } from '@blocksuite/affine-components/rich-text';
import type { TextElementModel } from '@blocksuite/affine-model'; import type { TextElementModel } from '@blocksuite/affine-model';
import { ThemeProvider } from '@blocksuite/affine-shared/services'; import { ThemeProvider } from '@blocksuite/affine-shared/services';
import { getSelectedRect } from '@blocksuite/affine-shared/utils';
import { import {
RANGE_SYNC_EXCLUDE_ATTR, RANGE_SYNC_EXCLUDE_ATTR,
ShadowlessElement, ShadowlessElement,
@@ -22,7 +23,6 @@ import { styleMap } from 'lit/directives/style-map.js';
import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js'; import type { EdgelessRootBlockComponent } from '../../edgeless-root-block.js';
import { deleteElements } from '../../utils/crud.js'; import { deleteElements } from '../../utils/crud.js';
import { getSelectedRect } from '../../utils/query.js';
const { toRadian } = CommonUtils; const { toRadian } = CommonUtils;
@@ -118,7 +118,7 @@ export const textRender: DraggableTool['render'] = (
}) as string; }) as string;
edgeless.doc.captureSync(); edgeless.doc.captureSync();
const textElement = edgeless.service.getElementById(id); const textElement = edgeless.service.crud.getElementById(id);
assertInstanceOf(textElement, TextElementModel); assertInstanceOf(textElement, TextElementModel);
mountTextElementEditor(textElement, edgeless); mountTextElementEditor(textElement, edgeless);
} }
@@ -186,9 +186,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin(
layoutType: mindmap?.layoutType === 'left' ? 1 : 0, layoutType: mindmap?.layoutType === 'left' ? 1 : 0,
}); });
if (!id) return; if (!id) return;
const element = this.edgeless.service.getElementById( const element = this.crud.getElementById(id) as MindmapElementModel;
id
) as MindmapElementModel;
this.tryDisposePopper(); this.tryDisposePopper();
this.setEdgelessTool({ type: 'default' }); this.setEdgelessTool({ type: 'default' });
@@ -372,7 +372,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
const node = mindmap.getNode(elements[0].id)!; const node = mindmap.getNode(elements[0].id)!;
const parent = mindmap.getParentNode(node.id) ?? node; const parent = mindmap.getParentNode(node.id) ?? node;
const id = mindmap.addNode(parent.id, currentNode.id, 'after'); const id = mindmap.addNode(parent.id, currentNode.id, 'after');
const target = service.getElementById(id) as ShapeElementModel; const target = service.crud.getElementById(id) as ShapeElementModel;
requestAnimationFrame(() => { requestAnimationFrame(() => {
mountShapeTextEditor(target, rootComponent); mountShapeTextEditor(target, rootComponent);
@@ -403,7 +403,7 @@ export class EdgelessPageKeyboardManager extends PageKeyboardManager {
const node = mindmap.getNode(elements[0].id)!; const node = mindmap.getNode(elements[0].id)!;
const id = mindmap.addNode(node.id); const id = mindmap.addNode(node.id);
const target = service.getElementById(id) as ShapeElementModel; const target = service.crud.getElementById(id) as ShapeElementModel;
if (node.detail.collapsed) { if (node.detail.collapsed) {
mindmap.toggleCollapse(node, { layout: true }); mindmap.toggleCollapse(node, { layout: true });
@@ -181,7 +181,7 @@ export class EdgelessRootPreviewBlockComponent extends BlockComponent<
); );
if (!surface) return; if (!surface) return;
const el = this.service.getElementById(surface.elements[0]); const el = this.service.crud.getElementById(surface.elements[0]);
if (isCanvasElement(el)) { if (isCanvasElement(el)) {
return true; return true;
} }
@@ -288,19 +288,6 @@ export class EdgelessRootService extends RootService implements SurfaceContext {
return this.surface.getConnectors(id) as ConnectorElementModel[]; return this.surface.getConnectors(id) as ConnectorElementModel[];
} }
getElementById(id: string): BlockSuite.EdgelessModel | null {
const el =
this._surface.getElementById(id) ??
(this.doc.getBlockById(id) as BlockSuite.EdgelessBlockModelType | null);
return el;
}
getElementsByType<K extends keyof BlockSuite.SurfaceElementModelMap>(
type: K
): BlockSuite.SurfaceElementModelMap[K][] {
return this.surface.getElementsByType(type);
}
getFitToScreenData( getFitToScreenData(
padding: [number, number, number, number] = [0, 0, 0, 0], padding: [number, number, number, number] = [0, 0, 0, 0],
inputBounds?: Bound[] inputBounds?: Bound[]
@@ -351,7 +338,7 @@ export class EdgelessRootService extends RootService implements SurfaceContext {
removeElement(id: string | BlockSuite.EdgelessModel) { removeElement(id: string | BlockSuite.EdgelessModel) {
id = typeof id === 'string' ? id : id.id; id = typeof id === 'string' ? id : id.id;
const el = this.getElementById(id); const el = this.crud.getElementById(id);
if (isGfxGroupCompatibleModel(el)) { if (isGfxGroupCompatibleModel(el)) {
el.childIds.forEach(childId => { el.childIds.forEach(childId => {
this.removeElement(childId); this.removeElement(childId);
@@ -4,12 +4,11 @@ import {
Overlay, Overlay,
type SurfaceBlockComponent, type SurfaceBlockComponent,
} from '@blocksuite/affine-block-surface'; } from '@blocksuite/affine-block-surface';
import { isTopLevelBlock } from '@blocksuite/affine-shared/utils';
import type { PointerEventState } from '@blocksuite/block-std'; import type { PointerEventState } from '@blocksuite/block-std';
import { BaseTool } from '@blocksuite/block-std/gfx'; import { BaseTool } from '@blocksuite/block-std/gfx';
import { Bound, type IVec } from '@blocksuite/global/utils'; import { Bound, type IVec } from '@blocksuite/global/utils';
import { isTopLevelBlock } from '../utils/query.js';
const { getSvgPathFromStroke, getStroke, linePolygonIntersects } = CommonUtils; const { getSvgPathFromStroke, getStroke, linePolygonIntersects } = CommonUtils;
class EraserOverlay extends Overlay { class EraserOverlay extends Overlay {
@@ -6,6 +6,7 @@ import type {
ImageBlockModel, ImageBlockModel,
NoteBlockModel, NoteBlockModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { getElementsWithoutGroup } from '@blocksuite/affine-shared/utils';
import { import {
generateKeyBetweenV2, generateKeyBetweenV2,
type SerializedElement, type SerializedElement,
@@ -16,7 +17,6 @@ import { type BlockSnapshot, BlockSnapshotSchema } from '@blocksuite/store';
import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js'; import type { EdgelessRootBlockComponent } from '../edgeless-root-block.js';
import { EdgelessFrameManager } from '../frame-manager.js'; import { EdgelessFrameManager } from '../frame-manager.js';
import { getSortedCloneElements, prepareCloneData } from './clone-utils.js'; import { getSortedCloneElements, prepareCloneData } from './clone-utils.js';
import { getElementsWithoutGroup } from './group.js';
import { import {
isEdgelessTextBlock, isEdgelessTextBlock,
isEmbedSyncedDocBlock, isEmbedSyncedDocBlock,
@@ -1,15 +0,0 @@
import { GroupElementModel } from '@blocksuite/affine-model';
export function getElementsWithoutGroup(elements: BlockSuite.EdgelessModel[]) {
const set = new Set<BlockSuite.EdgelessModel>();
elements.forEach(element => {
if (element instanceof GroupElementModel) {
element.descendantElements
.filter(descendant => !(descendant instanceof GroupElementModel))
.forEach(descendant => set.add(descendant));
} else {
set.add(element);
}
});
return Array.from(set);
}
@@ -17,13 +17,17 @@ import {
type EmbedLoomModel, type EmbedLoomModel,
type EmbedSyncedDocModel, type EmbedSyncedDocModel,
type EmbedYoutubeModel, type EmbedYoutubeModel,
FrameBlockModel, type FrameBlockModel,
type ImageBlockModel, type ImageBlockModel,
MindmapElementModel, MindmapElementModel,
type NoteBlockModel, type NoteBlockModel,
ShapeElementModel, ShapeElementModel,
TextElementModel, TextElementModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import {
getElementsWithoutGroup,
isTopLevelBlock,
} from '@blocksuite/affine-shared/utils';
import type { import type {
GfxBlockElementModel, GfxBlockElementModel,
GfxModel, GfxModel,
@@ -32,15 +36,10 @@ import type {
Viewport, Viewport,
} from '@blocksuite/block-std/gfx'; } from '@blocksuite/block-std/gfx';
import type { PointLocation } from '@blocksuite/global/utils'; import type { PointLocation } from '@blocksuite/global/utils';
import { import { Bound } from '@blocksuite/global/utils';
Bound,
deserializeXYWH,
getQuadBoundWithRotation,
} from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
import type { Connectable } from '../../../_common/utils/index.js'; import type { Connectable } from '../../../_common/utils/index.js';
import { getElementsWithoutGroup } from './group.js';
const { clamp } = CommonUtils; const { clamp } = CommonUtils;
@@ -50,12 +49,6 @@ export function isMindmapNode(
return element?.group instanceof MindmapElementModel; return element?.group instanceof MindmapElementModel;
} }
export function isTopLevelBlock(
selectable: BlockModel | BlockSuite.EdgelessModel | null
): selectable is GfxBlockElementModel {
return !!selectable && 'flavour' in selectable;
}
export function isNoteBlock( export function isNoteBlock(
element: BlockModel | BlockSuite.EdgelessModel | null element: BlockModel | BlockSuite.EdgelessModel | null
): element is NoteBlockModel { ): element is NoteBlockModel {
@@ -263,57 +256,6 @@ export function getBackgroundGrid(zoom: number, showGrid: boolean) {
}; };
} }
export function getSelectedRect(selected: BlockSuite.EdgelessModel[]): DOMRect {
if (selected.length === 0) {
return new DOMRect();
}
const lockedElementsByFrame = selected
.map(selectable => {
if (selectable instanceof FrameBlockModel && selectable.isLocked()) {
return selectable.descendantElements;
}
return [];
})
.flat();
selected = [...new Set([...selected, ...lockedElementsByFrame])];
if (selected.length === 1) {
const [x, y, w, h] = deserializeXYWH(selected[0].xywh);
return new DOMRect(x, y, w, h);
}
return getElementsWithoutGroup(selected).reduce(
(bounds, selectable, index) => {
const rotate = isTopLevelBlock(selectable) ? 0 : selectable.rotate;
const [x, y, w, h] = deserializeXYWH(selectable.xywh);
let { left, top, right, bottom } = getQuadBoundWithRotation({
x,
y,
w,
h,
rotate,
});
if (index !== 0) {
left = Math.min(left, bounds.left);
top = Math.min(top, bounds.top);
right = Math.max(right, bounds.right);
bottom = Math.max(bottom, bounds.bottom);
}
bounds.x = left;
bounds.y = top;
bounds.width = right - left;
bounds.height = bottom - top;
return bounds;
},
new DOMRect()
);
}
export type SelectableProps = { export type SelectableProps = {
bound: Bound; bound: Bound;
rotate: number; rotate: number;
@@ -79,7 +79,7 @@ export function mountShapeTextEditor(
.updateElement(shapeElement.id, { text }); .updateElement(shapeElement.id, { text });
} }
const updatedElement = edgeless.service.getElementById(shapeElement.id); const updatedElement = edgeless.service.crud.getElementById(shapeElement.id);
assertInstanceOf( assertInstanceOf(
updatedElement, updatedElement,
@@ -171,7 +171,7 @@ export function addText(
}); });
if (!id) return; if (!id) return;
edgeless.doc.captureSync(); edgeless.doc.captureSync();
const textElement = edgeless.service.getElementById(id); const textElement = edgeless.service.crud.getElementById(id);
if (!textElement) return; if (!textElement) return;
if (textElement instanceof TextElementModel) { if (textElement instanceof TextElementModel) {
mountTextElementEditor(textElement, edgeless); mountTextElementEditor(textElement, edgeless);
@@ -1,3 +1,4 @@
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import type { RootBlockModel } from '@blocksuite/affine-model'; import type { RootBlockModel } from '@blocksuite/affine-model';
import { DocModeProvider } from '@blocksuite/affine-shared/services'; import { DocModeProvider } from '@blocksuite/affine-shared/services';
import { import {
@@ -7,6 +8,7 @@ import {
getScrollContainer, getScrollContainer,
isInsideEdgelessEditor, isInsideEdgelessEditor,
isInsidePageEditor, isInsidePageEditor,
isTopLevelBlock,
matchFlavours, matchFlavours,
} from '@blocksuite/affine-shared/utils'; } from '@blocksuite/affine-shared/utils';
import { import {
@@ -22,9 +24,7 @@ import { html } from 'lit';
import { query, state } from 'lit/decorators.js'; import { query, state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import { isTopLevelBlock } from '../../../root-block/edgeless/utils/query.js';
import { autoScroll } from '../../../root-block/text-selection/utils.js'; import { autoScroll } from '../../../root-block/text-selection/utils.js';
import type { EdgelessRootService } from '../../edgeless/index.js';
import type { DragPreview } from './components/drag-preview.js'; import type { DragPreview } from './components/drag-preview.js';
import type { DropIndicator } from './components/drop-indicator.js'; import type { DropIndicator } from './components/drop-indicator.js';
import type { AFFINE_DRAG_HANDLE_WIDGET } from './consts.js'; import type { AFFINE_DRAG_HANDLE_WIDGET } from './consts.js';
@@ -196,8 +196,8 @@ export class AffineDragHandleWidget extends WidgetComponent<RootBlockModel> {
if (!this.anchorBlockId.value) return null; if (!this.anchorBlockId.value) return null;
if (this.mode === 'page') return null; if (this.mode === 'page') return null;
const service = this.std.getService('affine:page') as EdgelessRootService; const crud = this.std.get(EdgelessCRUDIdentifier);
const edgelessElement = service.getElementById(this.anchorBlockId.value); const edgelessElement = crud.getElementById(this.anchorBlockId.value);
return isTopLevelBlock(edgelessElement) ? edgelessElement : null; return isTopLevelBlock(edgelessElement) ? edgelessElement : null;
} }
); );
@@ -2,6 +2,10 @@ import {
EdgelessLegacySlotIdentifier, EdgelessLegacySlotIdentifier,
type SurfaceBlockComponent, type SurfaceBlockComponent,
} from '@blocksuite/affine-block-surface'; } from '@blocksuite/affine-block-surface';
import {
getSelectedRect,
isTopLevelBlock,
} from '@blocksuite/affine-shared/utils';
import type { DndEventState } from '@blocksuite/block-std'; import type { DndEventState } from '@blocksuite/block-std';
import { import {
GfxControllerIdentifier, GfxControllerIdentifier,
@@ -10,10 +14,6 @@ import {
import { type IVec, Rect } from '@blocksuite/global/utils'; import { type IVec, Rect } from '@blocksuite/global/utils';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
import {
getSelectedRect,
isTopLevelBlock,
} from '../../../edgeless/utils/query.js';
import { import {
DRAG_HANDLE_CONTAINER_OFFSET_LEFT_TOP_LEVEL, DRAG_HANDLE_CONTAINER_OFFSET_LEFT_TOP_LEVEL,
DRAG_HANDLE_CONTAINER_WIDTH_TOP_LEVEL, DRAG_HANDLE_CONTAINER_WIDTH_TOP_LEVEL,
@@ -194,7 +194,7 @@ export class EdgelessAutoConnectWidget extends WidgetComponent<
note.children.forEach(model => { note.children.forEach(model => {
if (matchFlavours(model, ['affine:surface-ref'])) { if (matchFlavours(model, ['affine:surface-ref'])) {
const reference = service.getElementById(model.reference); const reference = service.crud.getElementById(model.reference);
if (!isAutoConnectElement(reference)) return; if (!isAutoConnectElement(reference)) return;
@@ -1,28 +1,26 @@
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import { RemoteCursor } from '@blocksuite/affine-components/icons'; import { RemoteCursor } from '@blocksuite/affine-components/icons';
import type { RootBlockModel } from '@blocksuite/affine-model'; import type { RootBlockModel } from '@blocksuite/affine-model';
import { requestThrottledConnectedFrame } from '@blocksuite/affine-shared/utils'; import {
getSelectedRect,
isTopLevelBlock,
requestThrottledConnectedFrame,
} from '@blocksuite/affine-shared/utils';
import { WidgetComponent } from '@blocksuite/block-std'; import { WidgetComponent } from '@blocksuite/block-std';
import { assertExists, pickValues } from '@blocksuite/global/utils'; import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { pickValues } from '@blocksuite/global/utils';
import type { UserInfo } from '@blocksuite/store'; import type { UserInfo } from '@blocksuite/store';
import { css, html } from 'lit'; import { css, html, nothing } from 'lit';
import { state } from 'lit/decorators.js'; import { state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js'; import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import type { EdgelessRootBlockComponent } from '../../../root-block/edgeless/edgeless-root-block.js';
import {
getSelectedRect,
isTopLevelBlock,
} from '../../../root-block/edgeless/utils/query.js';
import { RemoteColorManager } from '../../../root-block/remote-color-manager/remote-color-manager.js'; import { RemoteColorManager } from '../../../root-block/remote-color-manager/remote-color-manager.js';
export const AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET = export const AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET =
'affine-edgeless-remote-selection-widget'; 'affine-edgeless-remote-selection-widget';
export class EdgelessRemoteSelectionWidget extends WidgetComponent< export class EdgelessRemoteSelectionWidget extends WidgetComponent<RootBlockModel> {
RootBlockModel,
EdgelessRootBlockComponent
> {
static override styles = css` static override styles = css`
:host { :host {
pointer-events: none; pointer-events: none;
@@ -109,7 +107,7 @@ export class EdgelessRemoteSelectionWidget extends WidgetComponent<
}; };
private readonly _updateRemoteRects = () => { private readonly _updateRemoteRects = () => {
const { selection, block } = this; const { selection } = this;
const remoteSelectionsMap = selection.remoteSurfaceSelectionsMap; const remoteSelectionsMap = selection.remoteSurfaceSelectionsMap;
const remoteRects: EdgelessRemoteSelectionWidget['_remoteRects'] = const remoteRects: EdgelessRemoteSelectionWidget['_remoteRects'] =
new Map(); new Map();
@@ -119,7 +117,7 @@ export class EdgelessRemoteSelectionWidget extends WidgetComponent<
if (selection.elements.length === 0) return; if (selection.elements.length === 0) return;
const elements = selection.elements const elements = selection.elements
.map(id => block.service.getElementById(id)) .map(id => this.crud.getElementById(id))
.filter(element => element) as BlockSuite.EdgelessModel[]; .filter(element => element) as BlockSuite.EdgelessModel[];
const rect = getSelectedRect(elements); const rect = getSelectedRect(elements);
@@ -151,7 +149,7 @@ export class EdgelessRemoteSelectionWidget extends WidgetComponent<
}; };
private readonly _updateTransform = requestThrottledConnectedFrame(() => { private readonly _updateTransform = requestThrottledConnectedFrame(() => {
const { translateX, translateY, zoom } = this.edgeless.service.viewport; const { translateX, translateY, zoom } = this.gfx.viewport;
this.style.setProperty('--v-zoom', `${zoom}`); this.style.setProperty('--v-zoom', `${zoom}`);
@@ -161,24 +159,28 @@ export class EdgelessRemoteSelectionWidget extends WidgetComponent<
); );
}, this); }, this);
get edgeless() { get gfx() {
return this.block; return this.std.get(GfxControllerIdentifier);
}
get crud() {
return this.std.get(EdgelessCRUDIdentifier);
} }
get selection() { get selection() {
return this.edgeless.service.selection; return this.gfx.selection;
} }
get surface() { get surface() {
return this.edgeless.surface; return this.gfx.surface;
} }
override connectedCallback() { override connectedCallback() {
super.connectedCallback(); super.connectedCallback();
const { _disposables, doc, edgeless } = this; const { _disposables, doc } = this;
pickValues(edgeless.service.surface, [ pickValues(this.surface!, [
'elementAdded', 'elementAdded',
'elementRemoved', 'elementRemoved',
'elementUpdated', 'elementUpdated',
@@ -196,7 +198,7 @@ export class EdgelessRemoteSelectionWidget extends WidgetComponent<
); );
_disposables.add( _disposables.add(
edgeless.service.viewport.viewportUpdated.on(() => { this.gfx.viewport.viewportUpdated.on(() => {
this._updateTransform(); this._updateTransform();
}) })
); );
@@ -209,7 +211,7 @@ export class EdgelessRemoteSelectionWidget extends WidgetComponent<
override render() { override render() {
const { _remoteRects, _remoteCursors, _remoteColorManager } = this; const { _remoteRects, _remoteCursors, _remoteColorManager } = this;
assertExists(_remoteColorManager); if (!_remoteColorManager) return nothing;
const rects = repeat( const rects = repeat(
_remoteRects.entries(), _remoteRects.entries(),
@@ -54,7 +54,7 @@ export class EdgelessLockButton extends SignalWatcher(
// release other elements from their groups and group with top element // release other elements from their groups and group with top element
otherElements.forEach(element => { otherElements.forEach(element => {
// eslint-disable-next-line // oxlint-disable-next-line unicorn/prefer-dom-node-remove
element.group?.removeChild(element); element.group?.removeChild(element);
topElement.group?.addChild(element); topElement.group?.addChild(element);
}); });
@@ -72,7 +72,7 @@ export class EdgelessLockButton extends SignalWatcher(
const groupId = service.createGroup([topElement, ...otherElements]); const groupId = service.createGroup([topElement, ...otherElements]);
if (groupId) { if (groupId) {
const group = service.getElementById(groupId); const group = service.crud.getElementById(groupId);
if (group) { if (group) {
group.lock(); group.lock();
this.edgeless.gfx.selection.set({ this.edgeless.gfx.selection.set({
@@ -1,9 +1,9 @@
import type { CanvasRenderer } from '@blocksuite/affine-block-surface'; import type { CanvasRenderer } from '@blocksuite/affine-block-surface';
import { isTopLevelBlock } from '@blocksuite/affine-shared/utils';
import type { EditorHost } from '@blocksuite/block-std'; import type { EditorHost } from '@blocksuite/block-std';
import { assertExists, Bound } from '@blocksuite/global/utils'; import { assertExists, Bound } from '@blocksuite/global/utils';
import { ExportManager } from '../../../_common/export-manager/export-manager.js'; import { ExportManager } from '../../../_common/export-manager/export-manager.js';
import { isTopLevelBlock } from '../../../root-block/edgeless/utils/query.js';
import type { SurfaceRefBlockComponent } from '../../../surface-ref-block/surface-ref-block.js'; import type { SurfaceRefBlockComponent } from '../../../surface-ref-block/surface-ref-block.js';
export const edgelessToBlob = async ( export const edgelessToBlob = async (
@@ -452,7 +452,8 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
const edgelessService = this.std.get(EdgelessRootService); const edgelessService = this.std.get(EdgelessRootService);
const { _disposable } = this; const { _disposable } = this;
const referenceElement = edgelessService.getElementById(referenceId); const referenceElement =
edgelessService.crud.getElementById(referenceId);
if (!referenceElement) { if (!referenceElement) {
throw new BlockSuiteError( throw new BlockSuiteError(
ErrorCode.MissingViewModelError, ErrorCode.MissingViewModelError,
@@ -121,7 +121,7 @@ describe('group', () => {
const groupId = service.crud.addElement('group', { children }); const groupId = service.crud.addElement('group', { children });
assertExists(groupId); assertExists(groupId);
const group = service.getElementById(groupId) as GroupElementModel; const group = service.crud.getElementById(groupId) as GroupElementModel;
const assertInitial = () => { const assertInitial = () => {
expect(group.x).toBe(0); expect(group.x).toBe(0);
expect(group.y).toBe(0); expect(group.y).toBe(0);
@@ -190,7 +190,7 @@ describe('group', () => {
const map = new DocCollection.Y.Map<boolean>(); const map = new DocCollection.Y.Map<boolean>();
const groupId = service.crud.addElement('group', { children: map }); const groupId = service.crud.addElement('group', { children: map });
assertExists(groupId); assertExists(groupId);
const group = service.getElementById(groupId) as GroupElementModel; const group = service.crud.getElementById(groupId) as GroupElementModel;
expect(group.x).toBe(0); expect(group.x).toBe(0);
expect(group.y).toBe(0); expect(group.y).toBe(0);
@@ -205,7 +205,7 @@ describe('group', () => {
}) as string; }) as string;
}); });
const groups = groupIds.map( const groups = groupIds.map(
id => service.getElementById(id) as GroupElementModel id => service.crud.getElementById(id) as GroupElementModel
); );
groups.forEach(group => { groups.forEach(group => {
@@ -255,7 +255,7 @@ describe('mindmap', () => {
const mindmapId = service.crud.addElement('mindmap', { children: tree }); const mindmapId = service.crud.addElement('mindmap', { children: tree });
assertExists(mindmapId); assertExists(mindmapId);
const mindmap = () => const mindmap = () =>
service.getElementById(mindmapId) as MindmapElementModel; service.crud.getElementById(mindmapId) as MindmapElementModel;
expect(service.surface.elementModels.length).toBe(6); expect(service.surface.elementModels.length).toBe(6);
doc.captureSync(); doc.captureSync();
@@ -306,7 +306,7 @@ describe('mindmap', () => {
}); });
assertExists(mindmapId); assertExists(mindmapId);
const mindmap = () => const mindmap = () =>
service.getElementById(mindmapId) as MindmapElementModel; service.crud.getElementById(mindmapId) as MindmapElementModel;
doc.captureSync(); doc.captureSync();
await wait(); await wait();
@@ -351,7 +351,7 @@ describe('mindmap', () => {
}); });
assertExists(mindmapId); assertExists(mindmapId);
const mindmap = () => const mindmap = () =>
service.getElementById(mindmapId) as MindmapElementModel; service.crud.getElementById(mindmapId) as MindmapElementModel;
doc.captureSync(); doc.captureSync();
await wait(); await wait();
@@ -50,7 +50,7 @@ describe('apply last props', () => {
shapeType: ShapeType.Rect, shapeType: ShapeType.Rect,
}); });
assertExists(rectId); assertExists(rectId);
const rectShape = service.getElementById(rectId) as ShapeElementModel; const rectShape = service.crud.getElementById(rectId) as ShapeElementModel;
expect(rectShape.fillColor).toBe(ShapeFillColor.Yellow); expect(rectShape.fillColor).toBe(ShapeFillColor.Yellow);
service.crud.updateElement(rectId, { service.crud.updateElement(rectId, {
fillColor: ShapeFillColor.Orange, fillColor: ShapeFillColor.Orange,
@@ -65,7 +65,9 @@ describe('apply last props', () => {
shapeType: ShapeType.Diamond, shapeType: ShapeType.Diamond,
}); });
assertExists(diamondId); assertExists(diamondId);
const diamondShape = service.getElementById(diamondId) as ShapeElementModel; const diamondShape = service.crud.getElementById(
diamondId
) as ShapeElementModel;
expect(diamondShape.fillColor).toBe(ShapeFillColor.Yellow); expect(diamondShape.fillColor).toBe(ShapeFillColor.Yellow);
service.crud.updateElement(diamondId, { service.crud.updateElement(diamondId, {
fillColor: ShapeFillColor.Blue, fillColor: ShapeFillColor.Blue,
@@ -81,7 +83,7 @@ describe('apply last props', () => {
radius: 0.1, radius: 0.1,
}); });
assertExists(roundedRectId); assertExists(roundedRectId);
const roundedRectShape = service.getElementById( const roundedRectShape = service.crud.getElementById(
roundedRectId roundedRectId
) as ShapeElementModel; ) as ShapeElementModel;
expect(roundedRectShape.fillColor).toBe(ShapeFillColor.Yellow); expect(roundedRectShape.fillColor).toBe(ShapeFillColor.Yellow);
@@ -97,14 +99,16 @@ describe('apply last props', () => {
shapeType: ShapeType.Rect, shapeType: ShapeType.Rect,
}); });
assertExists(rectId2); assertExists(rectId2);
const rectShape2 = service.getElementById(rectId2) as ShapeElementModel; const rectShape2 = service.crud.getElementById(
rectId2
) as ShapeElementModel;
expect(rectShape2.fillColor).toBe(ShapeFillColor.Orange); expect(rectShape2.fillColor).toBe(ShapeFillColor.Orange);
const diamondId2 = service.crud.addElement('shape', { const diamondId2 = service.crud.addElement('shape', {
shapeType: ShapeType.Diamond, shapeType: ShapeType.Diamond,
}); });
assertExists(diamondId2); assertExists(diamondId2);
const diamondShape2 = service.getElementById( const diamondShape2 = service.crud.getElementById(
diamondId2 diamondId2
) as ShapeElementModel; ) as ShapeElementModel;
expect(diamondShape2.fillColor).toBe(ShapeFillColor.Blue); expect(diamondShape2.fillColor).toBe(ShapeFillColor.Blue);
@@ -114,7 +118,7 @@ describe('apply last props', () => {
radius: 0.1, radius: 0.1,
}); });
assertExists(roundedRectId2); assertExists(roundedRectId2);
const roundedRectShape2 = service.getElementById( const roundedRectShape2 = service.crud.getElementById(
roundedRectId2 roundedRectId2
) as ShapeElementModel; ) as ShapeElementModel;
expect(roundedRectShape2.fillColor).toBe(ShapeFillColor.Green); expect(roundedRectShape2.fillColor).toBe(ShapeFillColor.Green);
@@ -123,7 +127,7 @@ describe('apply last props', () => {
test('connector', () => { test('connector', () => {
const id = service.crud.addElement('connector', { mode: 0 }); const id = service.crud.addElement('connector', { mode: 0 });
assertExists(id); assertExists(id);
const connector = service.getElementById(id) as ConnectorElementModel; const connector = service.crud.getElementById(id) as ConnectorElementModel;
expect(connector.stroke).toBe(LineColor.Grey); expect(connector.stroke).toBe(LineColor.Grey);
expect(connector.strokeWidth).toBe(2); expect(connector.strokeWidth).toBe(2);
expect(connector.strokeStyle).toBe('solid'); expect(connector.strokeStyle).toBe('solid');
@@ -133,7 +137,9 @@ describe('apply last props', () => {
const id2 = service.crud.addElement('connector', { mode: 1 }); const id2 = service.crud.addElement('connector', { mode: 1 });
assertExists(id2); assertExists(id2);
const connector2 = service.getElementById(id2) as ConnectorElementModel; const connector2 = service.crud.getElementById(
id2
) as ConnectorElementModel;
expect(connector2.strokeWidth).toBe(10); expect(connector2.strokeWidth).toBe(10);
service.crud.updateElement(id2, { service.crud.updateElement(id2, {
labelStyle: { labelStyle: {
@@ -144,7 +150,9 @@ describe('apply last props', () => {
const id3 = service.crud.addElement('connector', { mode: 1 }); const id3 = service.crud.addElement('connector', { mode: 1 });
assertExists(id3); assertExists(id3);
const connector3 = service.getElementById(id3) as ConnectorElementModel; const connector3 = service.crud.getElementById(
id3
) as ConnectorElementModel;
expect(connector3.strokeWidth).toBe(10); expect(connector3.strokeWidth).toBe(10);
expect(connector3.labelStyle.color).toBe(LineColor.Magenta); expect(connector3.labelStyle.color).toBe(LineColor.Magenta);
expect(connector3.labelStyle.fontFamily).toBe(FontFamily.Kalam); expect(connector3.labelStyle.fontFamily).toBe(FontFamily.Kalam);
@@ -153,14 +161,14 @@ describe('apply last props', () => {
test('brush', () => { test('brush', () => {
const id = service.crud.addElement('brush', {}); const id = service.crud.addElement('brush', {});
assertExists(id); assertExists(id);
const brush = service.getElementById(id) as BrushElementModel; const brush = service.crud.getElementById(id) as BrushElementModel;
expect(brush.color).toEqual({ expect(brush.color).toEqual({
dark: LineColor.White, dark: LineColor.White,
light: LineColor.Black, light: LineColor.Black,
}); });
expect(brush.lineWidth).toBe(4); expect(brush.lineWidth).toBe(4);
service.crud.updateElement(id, { lineWidth: 10 }); service.crud.updateElement(id, { lineWidth: 10 });
const secondBrush = service.getElementById( const secondBrush = service.crud.getElementById(
service.crud.addElement('brush', {}) as string service.crud.addElement('brush', {}) as string
) as BrushElementModel; ) as BrushElementModel;
expect(secondBrush.lineWidth).toBe(10); expect(secondBrush.lineWidth).toBe(10);
@@ -169,10 +177,10 @@ describe('apply last props', () => {
test('text', () => { test('text', () => {
const id = service.crud.addElement('text', {}); const id = service.crud.addElement('text', {});
assertExists(id); assertExists(id);
const text = service.getElementById(id) as TextElementModel; const text = service.crud.getElementById(id) as TextElementModel;
expect(text.fontSize).toBe(24); expect(text.fontSize).toBe(24);
service.crud.updateElement(id, { fontSize: 36 }); service.crud.updateElement(id, { fontSize: 36 });
const secondText = service.getElementById( const secondText = service.crud.getElementById(
service.crud.addElement('text', {}) as string service.crud.addElement('text', {}) as string
) as TextElementModel; ) as TextElementModel;
expect(secondText.fontSize).toBe(36); expect(secondText.fontSize).toBe(36);
@@ -181,7 +189,7 @@ describe('apply last props', () => {
test('mindmap', () => { test('mindmap', () => {
const id = service.crud.addElement('mindmap', {}); const id = service.crud.addElement('mindmap', {});
assertExists(id); assertExists(id);
const mindmap = service.getElementById(id) as MindmapElementModel; const mindmap = service.crud.getElementById(id) as MindmapElementModel;
expect(mindmap.layoutType).toBe(LayoutType.RIGHT); expect(mindmap.layoutType).toBe(LayoutType.RIGHT);
expect(mindmap.style).toBe(MindmapStyle.ONE); expect(mindmap.style).toBe(MindmapStyle.ONE);
service.crud.updateElement(id, { service.crud.updateElement(id, {
@@ -191,7 +199,7 @@ describe('apply last props', () => {
const id2 = service.crud.addElement('mindmap', {}); const id2 = service.crud.addElement('mindmap', {});
assertExists(id2); assertExists(id2);
const mindmap2 = service.getElementById(id2) as MindmapElementModel; const mindmap2 = service.crud.getElementById(id2) as MindmapElementModel;
expect(mindmap2.layoutType).toBe(LayoutType.BALANCE); expect(mindmap2.layoutType).toBe(LayoutType.BALANCE);
expect(mindmap2.style).toBe(MindmapStyle.THREE); expect(mindmap2.style).toBe(MindmapStyle.THREE);
}); });
@@ -200,7 +208,7 @@ describe('apply last props', () => {
const surface = getSurfaceBlock(doc); const surface = getSurfaceBlock(doc);
const id = service.crud.addBlock('affine:edgeless-text', {}, surface!.id); const id = service.crud.addBlock('affine:edgeless-text', {}, surface!.id);
assertExists(id); assertExists(id);
const text = service.getElementById(id) as EdgelessTextBlockModel; const text = service.crud.getElementById(id) as EdgelessTextBlockModel;
expect(text.color).toBe(DEFAULT_TEXT_COLOR); expect(text.color).toBe(DEFAULT_TEXT_COLOR);
expect(text.fontFamily).toBe(FontFamily.Inter); expect(text.fontFamily).toBe(FontFamily.Inter);
service.crud.updateElement(id, { service.crud.updateElement(id, {
@@ -210,7 +218,7 @@ describe('apply last props', () => {
const id2 = service.crud.addBlock('affine:edgeless-text', {}, surface!.id); const id2 = service.crud.addBlock('affine:edgeless-text', {}, surface!.id);
assertExists(id2); assertExists(id2);
const text2 = service.getElementById(id2) as EdgelessTextBlockModel; const text2 = service.crud.getElementById(id2) as EdgelessTextBlockModel;
expect(text2.color).toBe(LineColor.Green); expect(text2.color).toBe(LineColor.Green);
expect(text2.fontFamily).toBe(FontFamily.OrelegaOne); expect(text2.fontFamily).toBe(FontFamily.OrelegaOne);
}); });
@@ -218,7 +226,7 @@ describe('apply last props', () => {
test('note', () => { test('note', () => {
const id = service.crud.addBlock('affine:note', {}, doc.root!.id); const id = service.crud.addBlock('affine:note', {}, doc.root!.id);
assertExists(id); assertExists(id);
const note = service.getElementById(id) as NoteBlockModel; const note = service.crud.getElementById(id) as NoteBlockModel;
expect(note.background).toBe(DEFAULT_NOTE_BACKGROUND_COLOR); expect(note.background).toBe(DEFAULT_NOTE_BACKGROUND_COLOR);
expect(note.edgeless.style.shadowType).toBe(DEFAULT_NOTE_SHADOW); expect(note.edgeless.style.shadowType).toBe(DEFAULT_NOTE_SHADOW);
service.crud.updateElement(id, { service.crud.updateElement(id, {
@@ -232,7 +240,7 @@ describe('apply last props', () => {
const id2 = service.crud.addBlock('affine:note', {}, doc.root!.id); const id2 = service.crud.addBlock('affine:note', {}, doc.root!.id);
assertExists(id2); assertExists(id2);
const note2 = service.getElementById(id2) as NoteBlockModel; const note2 = service.crud.getElementById(id2) as NoteBlockModel;
expect(note2.background).toBe(NoteBackgroundColor.Purple); expect(note2.background).toBe(NoteBackgroundColor.Purple);
expect(note2.edgeless.style.shadowType).toBe(NoteShadow.Film); expect(note2.edgeless.style.shadowType).toBe(NoteShadow.Film);
}); });
@@ -241,7 +249,7 @@ describe('apply last props', () => {
const surface = getSurfaceBlock(doc); const surface = getSurfaceBlock(doc);
const id = service.crud.addBlock('affine:frame', {}, surface!.id); const id = service.crud.addBlock('affine:frame', {}, surface!.id);
assertExists(id); assertExists(id);
const note = service.getElementById(id) as FrameBlockModel; const note = service.crud.getElementById(id) as FrameBlockModel;
expect(note.background).toBe('--affine-palette-transparent'); expect(note.background).toBe('--affine-palette-transparent');
service.crud.updateElement(id, { service.crud.updateElement(id, {
background: FrameBackgroundColor.Purple, background: FrameBackgroundColor.Purple,
@@ -249,7 +257,7 @@ describe('apply last props', () => {
const id2 = service.crud.addBlock('affine:frame', {}, surface!.id); const id2 = service.crud.addBlock('affine:frame', {}, surface!.id);
assertExists(id2); assertExists(id2);
const frame2 = service.getElementById(id2) as FrameBlockModel; const frame2 = service.crud.getElementById(id2) as FrameBlockModel;
expect(frame2.background).toBe(FrameBackgroundColor.Purple); expect(frame2.background).toBe(FrameBackgroundColor.Purple);
service.crud.updateElement(id2, { service.crud.updateElement(id2, {
background: { normal: '#def4e740' }, background: { normal: '#def4e740' },
@@ -257,7 +265,7 @@ describe('apply last props', () => {
const id3 = service.crud.addBlock('affine:frame', {}, surface!.id); const id3 = service.crud.addBlock('affine:frame', {}, surface!.id);
assertExists(id3); assertExists(id3);
const frame3 = service.getElementById(id3) as FrameBlockModel; const frame3 = service.crud.getElementById(id3) as FrameBlockModel;
expect(frame3.background).toEqual({ normal: '#def4e740' }); expect(frame3.background).toEqual({ normal: '#def4e740' });
service.crud.updateElement(id3, { service.crud.updateElement(id3, {
background: { light: '#a381aa23', dark: '#6e907452' }, background: { light: '#a381aa23', dark: '#6e907452' },
@@ -265,7 +273,7 @@ describe('apply last props', () => {
const id4 = service.crud.addBlock('affine:frame', {}, surface!.id); const id4 = service.crud.addBlock('affine:frame', {}, surface!.id);
assertExists(id4); assertExists(id4);
const frame4 = service.getElementById(id4) as FrameBlockModel; const frame4 = service.crud.getElementById(id4) as FrameBlockModel;
expect(frame4.background).toEqual({ expect(frame4.background).toEqual({
light: '#a381aa23', light: '#a381aa23',
dark: '#6e907452', dark: '#6e907452',
@@ -91,7 +91,7 @@ test('change element should update layer automatically', async () => {
service.crud.updateElement(id, { service.crud.updateElement(id, {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(id)!, service.crud.getElementById(id)!,
'forward' 'forward'
), ),
}); });
@@ -101,7 +101,7 @@ test('change element should update layer automatically', async () => {
service.crud.updateElement(canvasElId!, { service.crud.updateElement(canvasElId!, {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(canvasElId!)!, service.crud.getElementById(canvasElId!)!,
'forward' 'forward'
), ),
}); });
@@ -160,7 +160,7 @@ test('a new layer should be created in canvasLayers prop when the topmost layer
test('layer zindex should update correctly when elements changed', async () => { test('layer zindex should update correctly when elements changed', async () => {
addNote(doc); addNote(doc);
const noteId = addNote(doc); const noteId = addNote(doc);
const note = service.getElementById(noteId); const note = service.crud.getElementById(noteId);
addNote(doc); addNote(doc);
service.crud.addElement('shape', { service.crud.addElement('shape', {
shapeType: 'rect', shapeType: 'rect',
@@ -168,7 +168,7 @@ test('layer zindex should update correctly when elements changed', async () => {
const topShapeId = service.crud.addElement('shape', { const topShapeId = service.crud.addElement('shape', {
shapeType: 'rect', shapeType: 'rect',
}); });
const topShape = service.getElementById(topShapeId!)!; const topShape = service.crud.getElementById(topShapeId!)!;
await wait(); await wait();
@@ -238,8 +238,8 @@ test('blocks should rerender when their z-index changed', async () => {
service.crud.addElement('shape', { service.crud.addElement('shape', {
shapeType: 'rect', shapeType: 'rect',
index: CommonUtils.generateKeyBetween( index: CommonUtils.generateKeyBetween(
service.getElementById(blocks[1])!.index, service.crud.getElementById(blocks[1])!.index,
service.getElementById(blocks[2])!.index service.crud.getElementById(blocks[2])!.index
), ),
}); });
@@ -266,19 +266,19 @@ describe('layer reorder functionality', () => {
test('forward', async () => { test('forward', async () => {
service.crud.updateElement(ids[0], { service.crud.updateElement(ids[0], {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(ids[0])!, service.crud.getElementById(ids[0])!,
'forward' 'forward'
), ),
}); });
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[0]) as any) layer.set.has(service.crud.getElementById(ids[0]) as any)
) )
).toBe(1); ).toBe(1);
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[1]) as any) layer.set.has(service.crud.getElementById(ids[1]) as any)
) )
).toBe(0); ).toBe(0);
@@ -286,19 +286,19 @@ describe('layer reorder functionality', () => {
service.crud.updateElement(ids[1], { service.crud.updateElement(ids[1], {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(ids[1])!, service.crud.getElementById(ids[1])!,
'forward' 'forward'
), ),
}); });
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[0]) as any) layer.set.has(service.crud.getElementById(ids[0]) as any)
) )
).toBe(0); ).toBe(0);
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[1]) as any) layer.set.has(service.crud.getElementById(ids[1]) as any)
) )
).toBe(1); ).toBe(1);
}); });
@@ -306,7 +306,7 @@ describe('layer reorder functionality', () => {
test('front', async () => { test('front', async () => {
service.crud.updateElement(ids[0], { service.crud.updateElement(ids[0], {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(ids[0])!, service.crud.getElementById(ids[0])!,
'front' 'front'
), ),
}); });
@@ -315,20 +315,20 @@ describe('layer reorder functionality', () => {
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[0]) as any) layer.set.has(service.crud.getElementById(ids[0]) as any)
) )
).toBe(3); ).toBe(3);
service.crud.updateElement(ids[1], { service.crud.updateElement(ids[1], {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(ids[1])!, service.crud.getElementById(ids[1])!,
'front' 'front'
), ),
}); });
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[1]) as any) layer.set.has(service.crud.getElementById(ids[1]) as any)
) )
).toBe(3); ).toBe(3);
}); });
@@ -336,19 +336,19 @@ describe('layer reorder functionality', () => {
test('backward', async () => { test('backward', async () => {
service.crud.updateElement(ids[3], { service.crud.updateElement(ids[3], {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(ids[3])!, service.crud.getElementById(ids[3])!,
'backward' 'backward'
), ),
}); });
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[3]) as any) layer.set.has(service.crud.getElementById(ids[3]) as any)
) )
).toBe(1); ).toBe(1);
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[2]) as any) layer.set.has(service.crud.getElementById(ids[2]) as any)
) )
).toBe(2); ).toBe(2);
@@ -356,19 +356,19 @@ describe('layer reorder functionality', () => {
service.crud.updateElement(ids[2], { service.crud.updateElement(ids[2], {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(ids[2])!, service.crud.getElementById(ids[2])!,
'backward' 'backward'
), ),
}); });
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[3]) as any) layer.set.has(service.crud.getElementById(ids[3]) as any)
) )
).toBe(3); ).toBe(3);
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[2]) as any) layer.set.has(service.crud.getElementById(ids[2]) as any)
) )
).toBe(2); ).toBe(2);
}); });
@@ -376,14 +376,14 @@ describe('layer reorder functionality', () => {
test('back', async () => { test('back', async () => {
service.crud.updateElement(ids[3], { service.crud.updateElement(ids[3], {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(ids[3])!, service.crud.getElementById(ids[3])!,
'back' 'back'
), ),
}); });
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[3]) as any) layer.set.has(service.crud.getElementById(ids[3]) as any)
) )
).toBe(0); ).toBe(0);
@@ -391,14 +391,14 @@ describe('layer reorder functionality', () => {
service.crud.updateElement(ids[2], { service.crud.updateElement(ids[2], {
index: service.layer.getReorderedIndex( index: service.layer.getReorderedIndex(
service.getElementById(ids[2])!, service.crud.getElementById(ids[2])!,
'back' 'back'
), ),
}); });
expect( expect(
service.layer.layers.findIndex(layer => service.layer.layers.findIndex(layer =>
layer.set.has(service.getElementById(ids[2]) as any) layer.set.has(service.crud.getElementById(ids[2]) as any)
) )
).toBe(0); ).toBe(0);
}); });
@@ -507,7 +507,7 @@ describe('group related functionality', () => {
service, service,
elements.filter((_, idx) => idx !== 1 && idx !== 3) elements.filter((_, idx) => idx !== 1 && idx !== 3)
)!; )!;
const group = service.getElementById(groupId)!; const group = service.crud.getElementById(groupId)!;
expect(service.layer.layers.length).toBe(2); expect(service.layer.layers.length).toBe(2);
@@ -538,7 +538,7 @@ describe('group related functionality', () => {
})!, })!,
]; ];
service.doc.captureSync(); service.doc.captureSync();
const elements = elementIds.map(id => service.getElementById(id)!); const elements = elementIds.map(id => service.crud.getElementById(id)!);
const isKeptRelativeOrder = () => { const isKeptRelativeOrder = () => {
return elements.every((element, idx) => { return elements.every((element, idx) => {
@@ -552,7 +552,7 @@ describe('group related functionality', () => {
const groupId = createGroup(edgeless.service, elementIds)!; const groupId = createGroup(edgeless.service, elementIds)!;
expect(isKeptRelativeOrder()).toBeTruthy(); expect(isKeptRelativeOrder()).toBeTruthy();
service.ungroup(service.getElementById(groupId) as GroupElementModel); service.ungroup(service.crud.getElementById(groupId) as GroupElementModel);
expect(isKeptRelativeOrder()).toBeTruthy(); expect(isKeptRelativeOrder()).toBeTruthy();
service.doc.undo(); service.doc.undo();
@@ -586,15 +586,15 @@ describe('compare function', () => {
const shapeId = service.crud.addElement('shape', { const shapeId = service.crud.addElement('shape', {
shapeType: 'rect', shapeType: 'rect',
})!; })!;
const shapeEl = service.getElementById(shapeId)!; const shapeEl = service.crud.getElementById(shapeId)!;
expect(service.layer.compare(shapeEl, shapeEl)).toBe(SORT_ORDER.SAME); expect(service.layer.compare(shapeEl, shapeEl)).toBe(SORT_ORDER.SAME);
const groupId = createGroup(service, [shapeId])!; const groupId = createGroup(service, [shapeId])!;
const groupEl = service.getElementById(groupId)!; const groupEl = service.crud.getElementById(groupId)!;
expect(service.layer.compare(groupEl, groupEl)).toBe(SORT_ORDER.SAME); expect(service.layer.compare(groupEl, groupEl)).toBe(SORT_ORDER.SAME);
const noteId = addNote(doc); const noteId = addNote(doc);
const note = service.getElementById(noteId)! as NoteBlockModel; const note = service.crud.getElementById(noteId)! as NoteBlockModel;
expect(service.layer.compare(note, note)).toBe(SORT_ORDER.SAME); expect(service.layer.compare(note, note)).toBe(SORT_ORDER.SAME);
}); });
@@ -602,11 +602,11 @@ describe('compare function', () => {
const shapeId = service.crud.addElement('shape', { const shapeId = service.crud.addElement('shape', {
shapeType: 'rect', shapeType: 'rect',
})!; })!;
const shapeEl = service.getElementById(shapeId)!; const shapeEl = service.crud.getElementById(shapeId)!;
const noteId = addNote(doc); const noteId = addNote(doc);
const note = service.getElementById(noteId)! as NoteBlockModel; const note = service.crud.getElementById(noteId)! as NoteBlockModel;
const groupId = createGroup(service, [shapeId, noteId])!; const groupId = createGroup(service, [shapeId, noteId])!;
const groupEl = service.getElementById(groupId)!; const groupEl = service.crud.getElementById(groupId)!;
expect(service.layer.compare(groupEl, shapeEl)).toBe(SORT_ORDER.BEFORE); expect(service.layer.compare(groupEl, shapeEl)).toBe(SORT_ORDER.BEFORE);
expect(service.layer.compare(shapeEl, groupEl)).toBe(SORT_ORDER.AFTER); expect(service.layer.compare(shapeEl, groupEl)).toBe(SORT_ORDER.AFTER);
@@ -618,16 +618,16 @@ describe('compare function', () => {
const shape1Id = service.crud.addElement('shape', { const shape1Id = service.crud.addElement('shape', {
shapeType: 'rect', shapeType: 'rect',
})!; })!;
const shape1 = service.getElementById(shape1Id)!; const shape1 = service.crud.getElementById(shape1Id)!;
const shape2Id = service.crud.addElement('shape', { const shape2Id = service.crud.addElement('shape', {
shapeType: 'rect', shapeType: 'rect',
})!; })!;
const shape2 = service.getElementById(shape2Id)!; const shape2 = service.crud.getElementById(shape2Id)!;
const note1Id = addNote(doc); const note1Id = addNote(doc);
const note1 = service.getElementById(note1Id)! as NoteBlockModel; const note1 = service.crud.getElementById(note1Id)! as NoteBlockModel;
const note2Id = addNote(doc); const note2Id = addNote(doc);
const note2 = service.getElementById(note2Id)! as NoteBlockModel; const note2 = service.crud.getElementById(note2Id)! as NoteBlockModel;
expect(service.layer.compare(shape1, shape2)).toBe(SORT_ORDER.BEFORE); expect(service.layer.compare(shape1, shape2)).toBe(SORT_ORDER.BEFORE);
expect(service.layer.compare(shape2, shape1)).toBe(SORT_ORDER.AFTER); expect(service.layer.compare(shape2, shape1)).toBe(SORT_ORDER.AFTER);
@@ -659,12 +659,12 @@ describe('compare function', () => {
])!; ])!;
const group2Id = createGroup(service, [group1Id])!; const group2Id = createGroup(service, [group1Id])!;
const shape1 = service.getElementById(shape1Id)!; const shape1 = service.crud.getElementById(shape1Id)!;
const shape2 = service.getElementById(shape2Id)!; const shape2 = service.crud.getElementById(shape2Id)!;
const note1 = service.getElementById(note1Id)! as NoteBlockModel; const note1 = service.crud.getElementById(note1Id)! as NoteBlockModel;
const note2 = service.getElementById(note2Id)! as NoteBlockModel; const note2 = service.crud.getElementById(note2Id)! as NoteBlockModel;
const group1 = service.getElementById(group1Id)!; const group1 = service.crud.getElementById(group1Id)!;
const group2 = service.getElementById(group2Id)!; const group2 = service.crud.getElementById(group2Id)!;
// assert nested group to group // assert nested group to group
expect(service.layer.compare(group2, group1)).toBe(SORT_ORDER.BEFORE); expect(service.layer.compare(group2, group1)).toBe(SORT_ORDER.BEFORE);
@@ -695,9 +695,9 @@ describe('compare function', () => {
const groupAId = createGroup(service, [ const groupAId = createGroup(service, [
createGroup(service, [groupAShapeId, groupANoteId])!, createGroup(service, [groupAShapeId, groupANoteId])!,
])!; ])!;
const groupAShape = service.getElementById(groupAShapeId)!; const groupAShape = service.crud.getElementById(groupAShapeId)!;
const groupANote = service.getElementById(groupANoteId)!; const groupANote = service.crud.getElementById(groupANoteId)!;
const groupA = service.getElementById(groupAId)!; const groupA = service.crud.getElementById(groupAId)!;
const groupBShapeId = service.crud.addElement('shape', { const groupBShapeId = service.crud.addElement('shape', {
shapeType: 'rect', shapeType: 'rect',
@@ -706,9 +706,9 @@ describe('compare function', () => {
const groupBId = createGroup(service, [ const groupBId = createGroup(service, [
createGroup(service, [groupBShapeId, groupBNoteId])!, createGroup(service, [groupBShapeId, groupBNoteId])!,
])!; ])!;
const groupBShape = service.getElementById(groupBShapeId)!; const groupBShape = service.crud.getElementById(groupBShapeId)!;
const groupBNote = service.getElementById(groupBNoteId)!; const groupBNote = service.crud.getElementById(groupBNoteId)!;
const groupB = service.getElementById(groupBId)!; const groupB = service.crud.getElementById(groupBId)!;
expect(service.layer.compare(groupAShape, groupBShape)).toBe( expect(service.layer.compare(groupAShape, groupBShape)).toBe(
SORT_ORDER.BEFORE SORT_ORDER.BEFORE
@@ -860,10 +860,10 @@ describe('index generator', () => {
})!; })!;
const noteId = addNote(doc); const noteId = addNote(doc);
preinsertedShape = service.getElementById( preinsertedShape = service.crud.getElementById(
shapeId shapeId
)! as BlockSuite.SurfaceElementModel; )! as BlockSuite.SurfaceElementModel;
preinsertedNote = service.getElementById(noteId)! as NoteBlockModel; preinsertedNote = service.crud.getElementById(noteId)! as NoteBlockModel;
}); });
test('generator should remember the index it generated', () => { test('generator should remember the index it generated', () => {
@@ -211,7 +211,7 @@ describe('basic', () => {
const edgeless = surfaceRef.previewEditor!.std.get(EdgelessRootService); const edgeless = surfaceRef.previewEditor!.std.get(EdgelessRootService);
const group = edgeless.getElementById(groupId)!; const group = edgeless.crud.getElementById(groupId)!;
expect(edgeless.viewport.isInViewport(group.elementBound)).toBeTruthy(); expect(edgeless.viewport.isInViewport(group.elementBound)).toBeTruthy();
}); });
@@ -64,7 +64,7 @@ describe('default tool', () => {
drag(edgeless.host, { x: 0, y: 50 }, { x: 0, y: 150 }); drag(edgeless.host, { x: 0, y: 50 }, { x: 0, y: 150 });
await wait(); await wait();
const element = service.getElementById(id!)!; const element = service.crud.getElementById(id!)!;
expect(element.xywh).toEqual(`[0,100,100,100]`); expect(element.xywh).toEqual(`[0,100,100,100]`);
}); });
@@ -86,7 +86,7 @@ describe('default tool', () => {
drag(edgeless.host, { x: 50, y: 50 }, { x: 150, y: 150 }); drag(edgeless.host, { x: 50, y: 50 }, { x: 150, y: 150 });
await wait(); await wait();
const element = service.getElementById(noteId)!; const element = service.crud.getElementById(noteId)!;
const [x, y] = JSON.parse(element.xywh); const [x, y] = JSON.parse(element.xywh);
expect(x).toEqual(100); expect(x).toEqual(100);
@@ -124,7 +124,7 @@ test.describe('single edgeless element to linked doc', () => {
const shapes = await page.evaluate(() => { const shapes = await page.evaluate(() => {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
return container!.service return container!.service.crud
.getElementsByType('shape') .getElementsByType('shape')
.map(s => ({ type: s.type, xywh: s.xywh })); .map(s => ({ type: s.type, xywh: s.xywh }));
}); });
@@ -163,7 +163,7 @@ test.describe('single edgeless element to linked doc', () => {
await waitNextFrame(page, 200); await waitNextFrame(page, 200);
const brushes = await page.evaluate(() => { const brushes = await page.evaluate(() => {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
return container!.service return container!.service.crud
.getElementsByType('brush') .getElementsByType('brush')
.map(s => ({ type: s.type, xywh: s.xywh })); .map(s => ({ type: s.type, xywh: s.xywh }));
}); });
@@ -191,9 +191,9 @@ test.describe('single edgeless element to linked doc', () => {
await waitNextFrame(page, 200); await waitNextFrame(page, 200);
const groups = await page.evaluate(() => { const groups = await page.evaluate(() => {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
return container!.service.getElementsByType('group').map(s => ({ return container!.service.crud.getElementsByType('group').map(s => ({
type: s.type, type: s.type,
// eslint-disable-next-line @typescript-eslint/no-explicit-any // oxlint-disable-next-line @typescript-eslint/no-explicit-any
children: s.childElements.map((c: any) => c.type || c.flavour), children: s.childElements.map((c: any) => c.type || c.flavour),
})); }));
}); });
@@ -1005,7 +1005,7 @@ export async function deleteAllConnectors(page: Page) {
return page.evaluate(() => { return page.evaluate(() => {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
if (!container) throw new Error('container not found'); if (!container) throw new Error('container not found');
container.service.getElementsByType('connector').forEach(c => { container.service.crud.getElementsByType('connector').forEach(c => {
container.service.removeElement(c.id); container.service.removeElement(c.id);
}); });
}); });
@@ -1604,7 +1604,7 @@ export async function getConnectorSourceConnection(page: Page) {
return page.evaluate(() => { return page.evaluate(() => {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
if (!container) throw new Error('container not found'); if (!container) throw new Error('container not found');
return container.service.getElementsByType('connector')[0].source; return container.service.crud.getElementsByType('connector')[0].source;
}); });
} }
@@ -1613,7 +1613,7 @@ export async function getConnectorPath(page: Page, index = 0): Promise<IVec[]> {
([index]) => { ([index]) => {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
if (!container) throw new Error('container not found'); if (!container) throw new Error('container not found');
const connectors = container.service.getElementsByType('connector'); const connectors = container.service.crud.getElementsByType('connector');
return connectors[index].absolutePath; return connectors[index].absolutePath;
}, },
[index] [index]
@@ -1628,7 +1628,7 @@ export async function getEdgelessElementBound(
([elementId]) => { ([elementId]) => {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
if (!container) throw new Error('container not found'); if (!container) throw new Error('container not found');
const element = container.service.getElementById(elementId); const element = container.service.crud.getElementById(elementId);
if (!element) throw new Error(`element not found: ${elementId}`); if (!element) throw new Error(`element not found: ${elementId}`);
return JSON.parse(element.xywh); return JSON.parse(element.xywh);
}, },
@@ -1692,7 +1692,7 @@ export async function getContainerChildIds(page: Page, id: string) {
([id]) => { ([id]) => {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
if (!container) throw new Error('container not found'); if (!container) throw new Error('container not found');
const gfxModel = container.service.getElementById(id); const gfxModel = container.service.crud.getElementById(id);
return gfxModel && container.service.surface.isGroup(gfxModel) return gfxModel && container.service.surface.isGroup(gfxModel)
? gfxModel.childIds ? gfxModel.childIds
@@ -1731,7 +1731,7 @@ export async function getTypeById(page: Page, id: string) {
([id]) => { ([id]) => {
const container = document.querySelector('affine-edgeless-root'); const container = document.querySelector('affine-edgeless-root');
if (!container) throw new Error('container not found'); if (!container) throw new Error('container not found');
const element = container.service.getElementById(id)!; const element = container.service.crud.getElementById(id)!;
return 'flavour' in element ? element.flavour : element.type; return 'flavour' in element ? element.flavour : element.type;
}, },
[id] [id]
@@ -41,7 +41,7 @@ export function calcChildBound(
const targetBlocks = childConnectors const targetBlocks = childConnectors
.map(connector => connector.target.id) .map(connector => connector.target.id)
.filter(id => id !== undefined) .filter(id => id !== undefined)
.map(id => service.getElementById(id)) .map(id => service.crud.getElementById(id))
.filter(block => !!block); .filter(block => !!block);
if (targetBlocks.length) { if (targetBlocks.length) {