mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 14:28:51 +08:00
680f3b3006
#### PR Dependency Tree
* **PR #13464**
* **PR #13465**
* **PR #13471** 👈
* **PR #13472**
* **PR #13473**
This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* DOM rendering added for groups, mind maps and connectors so group
titles/outlines and mindmap connectors are visible on canvas.
* Shapes now support right-to-left text with proper vertical alignment.
* **Improvements**
* Connector labels scale with viewport zoom for crisper display.
* Group-related selections (including nested groups) now update visuals
consistently.
* **Performance**
* Reduced DOM churn and fewer redraws during rendering and selection
changes.
* **Refactor**
* Renderer import/export surfaces consolidated with no user-facing
behavior changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
68 lines
1.9 KiB
TypeScript
68 lines
1.9 KiB
TypeScript
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';
|
|
|
|
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]);
|
|
}
|