feat(editor): add edgeless crud extension (#9335)

This commit is contained in:
Saul-Mirone
2024-12-26 08:58:06 +00:00
parent 0de4f7abbb
commit 6afa1d542f
48 changed files with 629 additions and 423 deletions
@@ -1,4 +1,7 @@
import { updateXYWH } from '@blocksuite/affine-block-surface';
import {
EdgelessCRUDIdentifier,
updateXYWH,
} from '@blocksuite/affine-block-surface';
import {
AlignBottomIcon,
AlignDistributeHorizontallyIcon,
@@ -267,7 +270,7 @@ export class EdgelessAlignButton extends WithDisposable(LitElement) {
}
private _updateXYWH(ele: BlockSuite.EdgelessModel, bound: Bound) {
const { updateElement } = this.edgeless.service;
const { updateElement } = this.edgeless.std.get(EdgelessCRUDIdentifier);
const { updateBlock } = this.edgeless.doc;
updateXYWH(ele, bound, updateElement, updateBlock);
}
@@ -1,3 +1,4 @@
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import type {
BrushElementModel,
BrushProps,
@@ -57,10 +58,7 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) {
pickColor = (event: PickColorEvent) => {
if (event.type === 'pick') {
this.elements.forEach(ele =>
this.service.updateElement(
ele.id,
packColor('color', { ...event.detail })
)
this.crud.updateElement(ele.id, packColor('color', { ...event.detail }))
);
return;
}
@@ -93,6 +91,10 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) {
return this.edgeless.surface;
}
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
private _setBrushProp<K extends keyof BrushProps>(
key: K,
value: BrushProps[K]
@@ -101,7 +103,7 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) {
this.elements
.filter(notEqual(key, value))
.forEach(element =>
this.service.updateElement(element.id, { [key]: value })
this.crud.updateElement(element.id, { [key]: value })
);
}
@@ -1,3 +1,4 @@
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import {
AddTextIcon,
ConnectorCWithArrowIcon,
@@ -222,10 +223,14 @@ const MODE_CHOOSE: [ConnectorMode, () => TemplateResult<1>][] = [
] as const;
export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) {
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
pickColor = (event: PickColorEvent) => {
if (event.type === 'pick') {
this.elements.forEach(ele =>
this.service.updateElement(
this.crud.updateElement(
ele.id,
packColor('stroke', { ...event.detail })
)
@@ -257,7 +262,7 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) {
if (frontEndpointStyle === rearEndpointStyle) return;
this.elements.forEach(element =>
this.service.updateElement(element.id, {
this.crud.updateElement(element.id, {
frontEndpointStyle: rearEndpointStyle,
rearEndpointStyle: frontEndpointStyle,
})
@@ -286,7 +291,7 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) {
: 'rearEndpointStyle']: style,
};
this.elements.forEach(element =>
this.service.updateElement(element.id, { ...props })
this.crud.updateElement(element.id, { ...props })
);
}
@@ -297,7 +302,7 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) {
this.elements
.filter(notEqual(key, value))
.forEach(element =>
this.service.updateElement(element.id, { [key]: value })
this.crud.updateElement(element.id, { [key]: value })
);
}
@@ -2,6 +2,7 @@ import {
getDocContentWithMaxLength,
getEmbedCardIcons,
} from '@blocksuite/affine-block-embed';
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import {
CaptionIcon,
CenterPeekIcon,
@@ -114,6 +115,10 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) {
}
`;
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
private readonly _convertToCardView = () => {
if (this._isCardView) {
return;
@@ -146,7 +151,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) {
bound.w = EMBED_CARD_WIDTH[targetStyle];
bound.h = EMBED_CARD_HEIGHT[targetStyle];
const newId = this.edgeless.service.addBlock(
const newId = this.crud.addBlock(
targetFlavour,
{ url, xywh: bound.serialize(), style: targetStyle, caption },
this.edgeless.surface.model
@@ -197,7 +202,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) {
bound.w = EMBED_CARD_WIDTH[targetStyle];
bound.h = EMBED_CARD_HEIGHT[targetStyle];
const newId = this.edgeless.service.addBlock(
const newId = this.crud.addBlock(
flavour,
{
url,
@@ -206,6 +211,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) {
},
this.edgeless.surface.model
);
if (!newId) return;
this.std.command.exec('reassociateConnectors', {
oldId: id,
@@ -1,3 +1,4 @@
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import {
NoteIcon,
RenameIcon,
@@ -51,10 +52,14 @@ function getMostCommonColor(
}
export class EdgelessChangeFrameButton extends WithDisposable(LitElement) {
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
pickColor = (event: PickColorEvent) => {
if (event.type === 'pick') {
this.frames.forEach(ele =>
this.service.updateElement(
this.crud.updateElement(
ele.id,
packColor('background', { ...event.detail })
)
@@ -116,7 +121,7 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) {
private _setFrameBackground(color: string) {
this.frames.forEach(frame => {
this.service.updateElement(frame.id, { background: color });
this.crud.updateElement(frame.id, { background: color });
});
}
@@ -1,3 +1,4 @@
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import {
ExpandIcon,
LineStyleIcon,
@@ -78,6 +79,10 @@ function getMostCommonBackground(
}
export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
private readonly _setBorderRadius = (borderRadius: number) => {
this.notes.forEach(note => {
const props = {
@@ -88,7 +93,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
},
},
};
this.edgeless.service.updateElement(note.id, props);
this.crud.updateElement(note.id, props);
});
};
@@ -111,7 +116,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
if (event.type === 'pick') {
this.notes.forEach(element => {
const props = packColor('background', { ...event.detail });
this.edgeless.service.updateElement(element.id, props);
this.crud.updateElement(element.id, props);
});
return;
}
@@ -142,7 +147,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
private _setBackground(background: string) {
this.notes.forEach(element => {
this.edgeless.service.updateElement(element.id, { background });
this.crud.updateElement(element.id, { background });
});
}
@@ -173,7 +178,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
return;
}
this.edgeless.service.updateElement(note.id, { displayMode: newMode });
this.crud.updateElement(note.id, { displayMode: newMode });
const noteParent = this.doc.getParent(note);
assertExists(noteParent);
@@ -208,7 +213,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
},
},
};
this.edgeless.service.updateElement(note.id, props);
this.crud.updateElement(note.id, props);
});
}
@@ -222,7 +227,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
},
},
};
this.edgeless.service.updateElement(note.id, props);
this.crud.updateElement(note.id, props);
});
}
@@ -236,7 +241,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
},
},
};
this.edgeless.service.updateElement(note.id, props);
this.crud.updateElement(note.id, props);
});
}
@@ -1,3 +1,4 @@
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import {
AddTextIcon,
ChangeShapeIcon,
@@ -155,6 +156,10 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
return this.edgeless.service;
}
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
#pickColor<K extends keyof Pick<ShapeProps, 'fillColor' | 'strokeColor'>>(
key: K
) {
@@ -166,7 +171,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
if (key === 'fillColor' && !ele.filled) {
Object.assign(props, { filled: true });
}
this.service.updateElement(ele.id, props);
this.crud.updateElement(ele.id, props);
});
return;
}
@@ -199,25 +204,25 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
const filled = !isTransparent(fillColor);
const color = this._getTextColor(fillColor);
this.elements.forEach(ele =>
this.service.updateElement(ele.id, { filled, fillColor, color })
this.crud.updateElement(ele.id, { filled, fillColor, color })
);
}
private _setShapeStrokeColor(strokeColor: string) {
this.elements.forEach(ele =>
this.service.updateElement(ele.id, { strokeColor })
this.crud.updateElement(ele.id, { strokeColor })
);
}
private _setShapeStrokeStyle(strokeStyle: StrokeStyle) {
this.elements.forEach(ele =>
this.service.updateElement(ele.id, { strokeStyle })
this.crud.updateElement(ele.id, { strokeStyle })
);
}
private _setShapeStrokeWidth(strokeWidth: number) {
this.elements.forEach(ele =>
this.service.updateElement(ele.id, { strokeWidth })
this.crud.updateElement(ele.id, { strokeWidth })
);
}
@@ -226,7 +231,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
shapeStyle === ShapeStyle.General ? FontFamily.Inter : FontFamily.Kalam;
this.elements.forEach(ele => {
this.service.updateElement(ele.id, { shapeStyle, fontFamily });
this.crud.updateElement(ele.id, { shapeStyle, fontFamily });
});
}
@@ -257,7 +262,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
this._shapePanel.slots.select.on(shapeName => {
this.edgeless.doc.captureSync();
this.elements.forEach(element => {
this.service.updateElement(element.id, {
this.crud.updateElement(element.id, {
shapeType: getShapeType(shapeName),
radius: getShapeRadius(shapeName),
});
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import {
ConnectorUtils,
EdgelessCRUDIdentifier,
normalizeShapeBound,
TextUtils,
} from '@blocksuite/affine-block-surface';
@@ -176,6 +176,10 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
}
`;
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
private readonly _setFontFamily = (fontFamily: FontFamily) => {
const currentFontWeight = getMostCommonFontWeight(this.elements);
const fontWeight = TextUtils.isFontWeightSupported(
@@ -194,7 +198,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
const props = { fontFamily, fontWeight, fontStyle };
this.elements.forEach(element => {
this.service.updateElement(element.id, buildProps(element, props));
this.crud.updateElement(element.id, buildProps(element, props));
this._updateElementBound(element);
});
};
@@ -202,7 +206,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
private readonly _setFontSize = (fontSize: number) => {
const props = { fontSize };
this.elements.forEach(element => {
this.service.updateElement(element.id, buildProps(element, props));
this.crud.updateElement(element.id, buildProps(element, props));
this._updateElementBound(element);
});
};
@@ -213,7 +217,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
) => {
const props = { fontWeight, fontStyle };
this.elements.forEach(element => {
this.service.updateElement(element.id, buildProps(element, props));
this.crud.updateElement(element.id, buildProps(element, props));
this._updateElementBound(element);
});
};
@@ -221,14 +225,14 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
private readonly _setTextAlign = (textAlign: TextAlign) => {
const props = { textAlign };
this.elements.forEach(element => {
this.service.updateElement(element.id, buildProps(element, props));
this.crud.updateElement(element.id, buildProps(element, props));
});
};
private readonly _setTextColor = ({ detail: color }: ColorEvent) => {
const props = { color };
this.elements.forEach(element => {
this.service.updateElement(element.id, buildProps(element, props));
this.crud.updateElement(element.id, buildProps(element, props));
});
};
@@ -257,7 +261,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
},
Bound.fromXYWH(element.deserializedXYWH)
);
this.service.updateElement(element.id, {
this.crud.updateElement(element.id, {
xywh: newBound.serialize(),
});
} else if (
@@ -285,7 +289,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
prevBounds
);
bounds.center = center;
this.service.updateElement(element.id, {
this.crud.updateElement(element.id, {
labelXYWH: bounds.toXYWH(),
});
} else if (
@@ -296,7 +300,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
element,
Bound.fromXYWH(element.deserializedXYWH)
);
this.service.updateElement(element.id, {
this.crud.updateElement(element.id, {
xywh: newBound.serialize(),
});
}
@@ -307,7 +311,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
if (event.type === 'pick') {
this.elements.forEach(element => {
const props = packColor('color', { ...event.detail });
this.service.updateElement(element.id, buildProps(element, props));
this.crud.updateElement(element.id, buildProps(element, props));
this._updateElementBound(element);
});
return;
@@ -9,6 +9,7 @@ import {
promptDocTitle,
} from '@blocksuite/affine-block-embed';
import type { ImageBlockComponent } from '@blocksuite/affine-block-image';
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import { isPeekable, peek } from '@blocksuite/affine-components/peek';
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
@@ -253,8 +254,9 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
if (title === null) return;
const linkedDoc = createLinkedDocFromNote(doc, element, title);
const crud = std.get(EdgelessCRUDIdentifier);
// insert linked doc card
const cardId = service.addBlock(
const cardId = crud.addBlock(
'affine:embed-synced-doc',
{
xywh: element.xywh,
@@ -300,15 +302,7 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
icon: LinkedPageIcon({ width: '20', height: '20' }),
label: 'Create linked doc',
type: 'create-linked-doc',
action: async ({
doc,
selection,
service,
surface,
edgeless,
host,
std,
}) => {
action: async ({ doc, selection, surface, edgeless, host, std }) => {
const title = await promptDocTitle(std);
if (title === null) return;
@@ -318,6 +312,7 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
elements,
title
);
const crud = std.get(EdgelessCRUDIdentifier);
// delete selected elements
doc.transact(() => {
deleteElements(edgeless, elements);
@@ -326,7 +321,7 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
const width = 364;
const height = 390;
const bound = getCommonBoundWithRotation(elements);
const cardId = service.addBlock(
const cardId = crud.addBlock(
'affine:embed-linked-doc',
{
xywh: `[${bound.center[0] - width / 2}, ${bound.center[1] - height / 2}, ${width}, ${height}]`,