refactor(editor): separate the element renders (#11461)

This commit is contained in:
Saul-Mirone
2025-04-04 13:09:46 +00:00
parent 5a1106fb88
commit 2a1306c58c
70 changed files with 390 additions and 330 deletions

View File

@@ -12,6 +12,7 @@
"dependencies": {
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-gfx-text": "workspace:*",
"@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-rich-text": "workspace:*",
"@blocksuite/affine-shared": "workspace:*",

View File

@@ -0,0 +1,6 @@
import { FontFamily } from '@blocksuite/affine-model';
export const GROUP_TITLE_FONT = FontFamily.Inter;
export const GROUP_TITLE_FONT_SIZE = 12;
export const GROUP_TITLE_PADDING = [2, 0];
export const GROUP_TITLE_OFFSET = 4;

View File

@@ -0,0 +1,67 @@
import {
type CanvasRenderer,
type ElementRenderer,
ElementRendererExtension,
} from '@blocksuite/affine-block-surface';
import type { GroupElementModel } from '@blocksuite/affine-model';
import { Bound } from '@blocksuite/global/gfx';
import { titleRenderParams } from './utils.js';
export const group: ElementRenderer<GroupElementModel> = (
model,
ctx,
matrix,
renderer
) => {
const { xywh } = model;
const bound = Bound.deserialize(xywh);
const elements = renderer.provider.selectedElements?.() || [];
const renderParams = titleRenderParams(model, renderer.viewport.zoom);
model.externalXYWH = renderParams.titleBound.serialize();
ctx.setTransform(matrix);
if (elements.includes(model.id)) {
if (model.showTitle) {
renderTitle(model, ctx, renderer, renderParams);
} else {
ctx.lineWidth = 2 / renderer.viewport.zoom;
ctx.strokeStyle = renderer.getPropertyValue('--affine-blue');
ctx.strokeRect(0, 0, bound.w, bound.h);
}
} else if (model.childElements.some(child => elements.includes(child.id))) {
ctx.lineWidth = 2 / renderer.viewport.zoom;
ctx.strokeStyle = '#8FD1FF';
ctx.strokeRect(0, 0, bound.w, bound.h);
if (model.showTitle) renderTitle(model, ctx, renderer, renderParams);
}
};
export const GroupElementRendererExtension = ElementRendererExtension(
'group',
group
);
function renderTitle(
model: GroupElementModel,
ctx: CanvasRenderingContext2D,
renderer: CanvasRenderer,
renderParams: ReturnType<typeof titleRenderParams>
) {
const { text, lineHeight, font, padding, offset, titleBound } = renderParams;
model.externalXYWH = titleBound.serialize();
ctx.translate(0, -offset);
ctx.beginPath();
ctx.font = font;
ctx.fillStyle = renderer.getPropertyValue('--affine-blue');
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.fillText(text, padding[0], -lineHeight / 2 - padding[1]);
}

View File

@@ -0,0 +1,77 @@
import {
getFontString,
getLineHeight,
getLineWidth,
truncateTextByWidth,
} from '@blocksuite/affine-gfx-text';
import type { GroupElementModel } from '@blocksuite/affine-model';
import { FontWeight } from '@blocksuite/affine-model';
import { Bound } from '@blocksuite/global/gfx';
import {
GROUP_TITLE_FONT,
GROUP_TITLE_FONT_SIZE,
GROUP_TITLE_OFFSET,
GROUP_TITLE_PADDING,
} from './consts.js';
export function titleRenderParams(group: GroupElementModel, zoom: number) {
let text = group.title.toString().trim();
const font = getGroupTitleFont(zoom);
const lineWidth = getLineWidth(text, font);
const lineHeight = getLineHeight(
GROUP_TITLE_FONT,
GROUP_TITLE_FONT_SIZE / zoom,
'normal'
);
const bound = group.elementBound;
const padding = [
GROUP_TITLE_PADDING[0] / zoom,
GROUP_TITLE_PADDING[1] / zoom,
];
const offset = GROUP_TITLE_OFFSET / zoom;
let titleWidth = lineWidth + padding[0] * 2;
const titleHeight = lineHeight + padding[1] * 2;
if (titleWidth > bound.w) {
text = truncateTextByWidth(text, font, bound.w - 10);
text = text.slice(0, text.length - 1) + '..';
titleWidth = bound.w;
}
return {
font,
bound,
text,
titleWidth,
titleHeight,
offset,
lineHeight,
padding,
titleBound: new Bound(
bound.x,
bound.y - titleHeight - offset,
titleWidth,
titleHeight
),
};
}
export function titleBound(group: GroupElementModel, zoom: number) {
const { titleWidth, titleHeight, bound } = titleRenderParams(group, zoom);
return new Bound(bound.x, bound.y - titleHeight, titleWidth, titleHeight);
}
function getGroupTitleFont(zoom: number) {
const fontSize = GROUP_TITLE_FONT_SIZE / zoom;
const font = getFontString({
fontSize,
fontFamily: GROUP_TITLE_FONT,
fontWeight: FontWeight.Regular,
fontStyle: 'normal',
});
return font;
}

View File

@@ -1,4 +1,5 @@
export * from './command';
export * from './element-renderer';
export * from './text/text';
export * from './toolbar/config';
export * from './view';

View File

@@ -1,8 +1,3 @@
import {
GROUP_TITLE_FONT_SIZE,
GROUP_TITLE_OFFSET,
GROUP_TITLE_PADDING,
} from '@blocksuite/affine-block-surface';
import type { GroupElementModel } from '@blocksuite/affine-model';
import type { RichText } from '@blocksuite/affine-rich-text';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
@@ -21,6 +16,12 @@ import { html, nothing } from 'lit';
import { property, query } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
import {
GROUP_TITLE_FONT_SIZE,
GROUP_TITLE_OFFSET,
GROUP_TITLE_PADDING,
} from '../element-renderer/consts';
export function mountGroupTitleEditor(
group: GroupElementModel,
edgeless: BlockComponent

View File

@@ -9,6 +9,7 @@
"references": [
{ "path": "../../blocks/block-surface" },
{ "path": "../../components" },
{ "path": "../text" },
{ "path": "../../model" },
{ "path": "../../rich-text" },
{ "path": "../../shared" },