refactor(editor): separate the element renders (#11461)
This commit is contained in:
@@ -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:*",
|
||||
|
||||
308
blocksuite/affine/gfx/connector/src/element-renderer/index.ts
Normal file
308
blocksuite/affine/gfx/connector/src/element-renderer/index.ts
Normal file
@@ -0,0 +1,308 @@
|
||||
import {
|
||||
type CanvasRenderer,
|
||||
ConnectorUtils,
|
||||
type ElementRenderer,
|
||||
ElementRendererExtension,
|
||||
type RoughCanvas,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
getFontString,
|
||||
getLineHeight,
|
||||
getTextWidth,
|
||||
isRTL,
|
||||
type TextDelta,
|
||||
wrapTextDeltas,
|
||||
} from '@blocksuite/affine-gfx-text';
|
||||
import {
|
||||
type ConnectorElementModel,
|
||||
ConnectorMode,
|
||||
DefaultTheme,
|
||||
type LocalConnectorElementModel,
|
||||
type PointStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
getBezierParameters,
|
||||
type PointLocation,
|
||||
} from '@blocksuite/global/gfx';
|
||||
import { deltaInsertsToChunks } from '@blocksuite/std/inline';
|
||||
|
||||
import {
|
||||
DEFAULT_ARROW_SIZE,
|
||||
getArrowOptions,
|
||||
renderArrow,
|
||||
renderCircle,
|
||||
renderDiamond,
|
||||
renderTriangle,
|
||||
} from './utils.js';
|
||||
|
||||
export const connector: ElementRenderer<
|
||||
ConnectorElementModel | LocalConnectorElementModel
|
||||
> = (model, ctx, matrix, renderer, rc) => {
|
||||
const {
|
||||
mode,
|
||||
path: points,
|
||||
strokeStyle,
|
||||
frontEndpointStyle,
|
||||
rearEndpointStyle,
|
||||
strokeWidth,
|
||||
} = model;
|
||||
|
||||
// points might not be build yet in some senarios
|
||||
// eg. undo/redo, copy/paste
|
||||
if (!points.length || points.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.setTransform(matrix);
|
||||
|
||||
const hasLabel = ConnectorUtils.isConnectorWithLabel(model);
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
|
||||
if (hasLabel) {
|
||||
ctx.save();
|
||||
|
||||
const { deserializedXYWH, labelXYWH } = model as ConnectorElementModel;
|
||||
const [x, y, w, h] = deserializedXYWH;
|
||||
const [lx, ly, lw, lh] = labelXYWH!;
|
||||
const offset = DEFAULT_ARROW_SIZE * strokeWidth;
|
||||
|
||||
dx = lx - x;
|
||||
dy = ly - y;
|
||||
|
||||
const path = new Path2D();
|
||||
path.rect(-offset / 2, -offset / 2, w + offset, h + offset);
|
||||
path.rect(dx - 3 - 0.5, dy - 3 - 0.5, lw + 6 + 1, lh + 6 + 1);
|
||||
ctx.clip(path, 'evenodd');
|
||||
}
|
||||
|
||||
const strokeColor = renderer.getColorValue(
|
||||
model.stroke,
|
||||
DefaultTheme.connectorColor,
|
||||
true
|
||||
);
|
||||
|
||||
renderPoints(
|
||||
model,
|
||||
ctx,
|
||||
rc,
|
||||
points,
|
||||
strokeStyle === 'dash',
|
||||
mode === ConnectorMode.Curve,
|
||||
strokeColor
|
||||
);
|
||||
renderEndpoint(
|
||||
model,
|
||||
points,
|
||||
ctx,
|
||||
rc,
|
||||
'Front',
|
||||
frontEndpointStyle,
|
||||
strokeColor
|
||||
);
|
||||
renderEndpoint(
|
||||
model,
|
||||
points,
|
||||
ctx,
|
||||
rc,
|
||||
'Rear',
|
||||
rearEndpointStyle,
|
||||
strokeColor
|
||||
);
|
||||
|
||||
if (hasLabel) {
|
||||
ctx.restore();
|
||||
|
||||
renderLabel(
|
||||
model as ConnectorElementModel,
|
||||
ctx,
|
||||
matrix.translate(dx, dy),
|
||||
renderer
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const ConnectorElementRendererExtension = ElementRendererExtension(
|
||||
'connector',
|
||||
connector
|
||||
);
|
||||
|
||||
function renderPoints(
|
||||
model: ConnectorElementModel | LocalConnectorElementModel,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
rc: RoughCanvas,
|
||||
points: PointLocation[],
|
||||
dash: boolean,
|
||||
curve: boolean,
|
||||
stroke: string
|
||||
) {
|
||||
const { seed, strokeWidth, roughness, rough } = model;
|
||||
|
||||
if (rough) {
|
||||
const options = {
|
||||
seed,
|
||||
roughness,
|
||||
stroke,
|
||||
strokeLineDash: dash ? [12, 12] : undefined,
|
||||
strokeWidth,
|
||||
};
|
||||
if (curve) {
|
||||
const b = getBezierParameters(points);
|
||||
rc.path(
|
||||
`M${b[0][0]},${b[0][1]} C${b[1][0]},${b[1][1]} ${b[2][0]},${b[2][1]} ${b[3][0]},${b[3][1]}`,
|
||||
options
|
||||
);
|
||||
} else {
|
||||
rc.linearPath(points as unknown as [number, number][], options);
|
||||
}
|
||||
} else {
|
||||
ctx.save();
|
||||
ctx.strokeStyle = stroke;
|
||||
ctx.lineWidth = strokeWidth;
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineCap = 'round';
|
||||
dash && ctx.setLineDash([12, 12]);
|
||||
ctx.beginPath();
|
||||
if (curve) {
|
||||
points.forEach((point, index) => {
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point[0], point[1]);
|
||||
} else {
|
||||
const last = points[index - 1];
|
||||
ctx.bezierCurveTo(
|
||||
last.absOut[0],
|
||||
last.absOut[1],
|
||||
point.absIn[0],
|
||||
point.absIn[1],
|
||||
point[0],
|
||||
point[1]
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
points.forEach((point, index) => {
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point[0], point[1]);
|
||||
} else {
|
||||
ctx.lineTo(point[0], point[1]);
|
||||
}
|
||||
});
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
function renderEndpoint(
|
||||
model: ConnectorElementModel | LocalConnectorElementModel,
|
||||
location: PointLocation[],
|
||||
ctx: CanvasRenderingContext2D,
|
||||
rc: RoughCanvas,
|
||||
end: 'Front' | 'Rear',
|
||||
style: PointStyle,
|
||||
stroke: string
|
||||
) {
|
||||
const arrowOptions = getArrowOptions(end, model, stroke);
|
||||
|
||||
switch (style) {
|
||||
case 'Arrow':
|
||||
renderArrow(location, ctx, rc, arrowOptions);
|
||||
break;
|
||||
case 'Triangle':
|
||||
renderTriangle(location, ctx, rc, arrowOptions);
|
||||
break;
|
||||
case 'Circle':
|
||||
renderCircle(location, ctx, rc, arrowOptions);
|
||||
break;
|
||||
case 'Diamond':
|
||||
renderDiamond(location, ctx, rc, arrowOptions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function renderLabel(
|
||||
model: ConnectorElementModel,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
matrix: DOMMatrix,
|
||||
renderer: CanvasRenderer
|
||||
) {
|
||||
const {
|
||||
text,
|
||||
labelXYWH,
|
||||
labelStyle: {
|
||||
color,
|
||||
fontSize,
|
||||
fontWeight,
|
||||
fontStyle,
|
||||
fontFamily,
|
||||
textAlign,
|
||||
},
|
||||
labelConstraints: { hasMaxWidth, maxWidth },
|
||||
} = model;
|
||||
const font = getFontString({
|
||||
fontStyle,
|
||||
fontWeight,
|
||||
fontSize,
|
||||
fontFamily,
|
||||
});
|
||||
const [, , w, h] = labelXYWH!;
|
||||
const cx = w / 2;
|
||||
const cy = h / 2;
|
||||
const deltas = wrapTextDeltas(text!, font, w);
|
||||
const lines = deltaInsertsToChunks(deltas);
|
||||
const lineHeight = getLineHeight(fontFamily, fontSize, fontWeight);
|
||||
const textHeight = (lines.length - 1) * lineHeight * 0.5;
|
||||
|
||||
ctx.setTransform(matrix);
|
||||
|
||||
ctx.font = font;
|
||||
ctx.textAlign = textAlign;
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillStyle = renderer.getColorValue(color, DefaultTheme.black, true);
|
||||
|
||||
let textMaxWidth = textAlign === 'center' ? 0 : getMaxTextWidth(lines, font);
|
||||
if (hasMaxWidth && maxWidth > 0) {
|
||||
textMaxWidth = Math.min(textMaxWidth, textMaxWidth);
|
||||
}
|
||||
|
||||
for (const [index, line] of lines.entries()) {
|
||||
for (const delta of line) {
|
||||
const str = delta.insert;
|
||||
const rtl = isRTL(str);
|
||||
const shouldTemporarilyAttach = rtl && !ctx.canvas.isConnected;
|
||||
if (shouldTemporarilyAttach) {
|
||||
// to correctly render RTL text mixed with LTR, we have to append it
|
||||
// to the DOM
|
||||
document.body.append(ctx.canvas);
|
||||
}
|
||||
|
||||
ctx.canvas.setAttribute('dir', rtl ? 'rtl' : 'ltr');
|
||||
|
||||
const x =
|
||||
textMaxWidth *
|
||||
(textAlign === 'center'
|
||||
? 1
|
||||
: textAlign === 'right'
|
||||
? rtl
|
||||
? -0.5
|
||||
: 0.5
|
||||
: rtl
|
||||
? 0.5
|
||||
: -0.5);
|
||||
ctx.fillText(str, x + cx, index * lineHeight - textHeight + cy);
|
||||
|
||||
if (shouldTemporarilyAttach) {
|
||||
ctx.canvas.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getMaxTextWidth(lines: TextDelta[][], font: string) {
|
||||
return Math.max(
|
||||
...lines.flatMap(line =>
|
||||
line.map(delta => getTextWidth(delta.insert, font))
|
||||
)
|
||||
);
|
||||
}
|
||||
312
blocksuite/affine/gfx/connector/src/element-renderer/utils.ts
Normal file
312
blocksuite/affine/gfx/connector/src/element-renderer/utils.ts
Normal file
@@ -0,0 +1,312 @@
|
||||
import type { RoughCanvas } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
type ConnectorElementModel,
|
||||
ConnectorMode,
|
||||
type LocalConnectorElementModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import type {
|
||||
BezierCurveParameters,
|
||||
IVec,
|
||||
PointLocation,
|
||||
} from '@blocksuite/global/gfx';
|
||||
import {
|
||||
getBezierParameters,
|
||||
getBezierTangent,
|
||||
Vec,
|
||||
} from '@blocksuite/global/gfx';
|
||||
|
||||
type ConnectorEnd = 'Front' | 'Rear';
|
||||
|
||||
export const DEFAULT_ARROW_SIZE = 15;
|
||||
|
||||
export function getArrowPoints(
|
||||
points: PointLocation[],
|
||||
size = 10,
|
||||
mode: ConnectorMode,
|
||||
bezierParameters: BezierCurveParameters,
|
||||
endPoint: ConnectorEnd = 'Rear',
|
||||
radians: number = Math.PI / 4
|
||||
) {
|
||||
const anchorPoint = getPointWithTangent(
|
||||
points,
|
||||
mode,
|
||||
endPoint,
|
||||
bezierParameters
|
||||
);
|
||||
const unit = Vec.mul(anchorPoint.tangent, -1);
|
||||
const angle = endPoint === 'Front' ? Math.PI : 0;
|
||||
|
||||
return {
|
||||
points: [
|
||||
Vec.add(Vec.mul(Vec.rot(unit, angle + radians), size), anchorPoint),
|
||||
anchorPoint,
|
||||
Vec.add(Vec.mul(Vec.rot(unit, angle - radians), size), anchorPoint),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function getCircleCenterPoint(
|
||||
points: PointLocation[],
|
||||
radius = 5,
|
||||
mode: ConnectorMode,
|
||||
bezierParameters: BezierCurveParameters,
|
||||
endPoint: ConnectorEnd = 'Rear'
|
||||
) {
|
||||
const anchorPoint = getPointWithTangent(
|
||||
points,
|
||||
mode,
|
||||
endPoint,
|
||||
bezierParameters
|
||||
);
|
||||
|
||||
const unit = Vec.mul(anchorPoint.tangent, -1);
|
||||
const angle = endPoint === 'Front' ? Math.PI : 0;
|
||||
|
||||
return Vec.add(Vec.mul(Vec.rot(unit, angle), radius), anchorPoint);
|
||||
}
|
||||
|
||||
export function getPointWithTangent(
|
||||
points: PointLocation[],
|
||||
mode: ConnectorMode,
|
||||
endPoint: ConnectorEnd,
|
||||
bezierParameters: BezierCurveParameters
|
||||
) {
|
||||
const anchorIndex = endPoint === 'Rear' ? points.length - 1 : 0;
|
||||
const pointToAnchorIndex =
|
||||
endPoint === 'Rear' ? anchorIndex - 1 : anchorIndex + 1;
|
||||
const anchorPoint = points[anchorIndex];
|
||||
const pointToAnchor = points[pointToAnchorIndex];
|
||||
|
||||
const clone = anchorPoint.clone();
|
||||
let tangent;
|
||||
if (mode !== ConnectorMode.Curve) {
|
||||
tangent =
|
||||
endPoint === 'Rear'
|
||||
? Vec.tangent(anchorPoint, pointToAnchor)
|
||||
: Vec.tangent(pointToAnchor, anchorPoint);
|
||||
} else {
|
||||
tangent =
|
||||
endPoint === 'Rear'
|
||||
? getBezierTangent(bezierParameters, 1)
|
||||
: getBezierTangent(bezierParameters, 0);
|
||||
}
|
||||
clone.tangent = tangent ?? [0, 0];
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
export function getDiamondPoints(
|
||||
point: PointLocation,
|
||||
size = 10,
|
||||
endPoint: ConnectorEnd = 'Rear'
|
||||
) {
|
||||
const unit = Vec.mul(point.tangent, -1);
|
||||
const angle = endPoint === 'Front' ? Math.PI : 0;
|
||||
|
||||
const diamondPoints = [
|
||||
Vec.add(Vec.mul(Vec.rot(unit, angle + Math.PI * 0.25), size), point),
|
||||
point,
|
||||
Vec.add(Vec.mul(Vec.rot(unit, angle - Math.PI * 0.25), size), point),
|
||||
Vec.add(Vec.mul(Vec.rot(unit, angle), size * Math.sqrt(2)), point),
|
||||
];
|
||||
|
||||
return {
|
||||
points: diamondPoints,
|
||||
};
|
||||
}
|
||||
|
||||
export type ArrowOptions = ReturnType<typeof getArrowOptions>;
|
||||
|
||||
export function getArrowOptions(
|
||||
end: ConnectorEnd,
|
||||
model: ConnectorElementModel | LocalConnectorElementModel,
|
||||
strokeColor: string
|
||||
) {
|
||||
const { seed, mode, rough, roughness, strokeWidth, path } = model;
|
||||
|
||||
return {
|
||||
end,
|
||||
seed,
|
||||
mode,
|
||||
rough,
|
||||
roughness,
|
||||
strokeWidth,
|
||||
strokeColor,
|
||||
fillColor: strokeColor,
|
||||
fillStyle: 'solid',
|
||||
bezierParameters: getBezierParameters(path),
|
||||
};
|
||||
}
|
||||
|
||||
export function getRcOptions(options: ArrowOptions) {
|
||||
const { seed, roughness, strokeWidth, strokeColor, fillColor } = options;
|
||||
return {
|
||||
seed,
|
||||
roughness,
|
||||
stroke: strokeColor,
|
||||
strokeWidth,
|
||||
fill: fillColor,
|
||||
fillStyle: 'solid',
|
||||
};
|
||||
}
|
||||
|
||||
export function renderRoundedPolygon(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
points: IVec[],
|
||||
color: string,
|
||||
strokeWidth: number,
|
||||
fill: boolean = true
|
||||
) {
|
||||
ctx.fillStyle = color;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = strokeWidth;
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineCap = 'round';
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
if (i === 0) {
|
||||
ctx.moveTo(points[i][0], points[i][1]);
|
||||
} else {
|
||||
ctx.lineTo(points[i][0], points[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (fill) {
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
export function renderArrow(
|
||||
points: PointLocation[],
|
||||
ctx: CanvasRenderingContext2D,
|
||||
rc: RoughCanvas,
|
||||
options: ArrowOptions
|
||||
) {
|
||||
const { mode, end, bezierParameters, rough, strokeColor, strokeWidth } =
|
||||
options;
|
||||
const radians = Math.PI / 4;
|
||||
const size = DEFAULT_ARROW_SIZE * (strokeWidth / 2);
|
||||
const { points: arrowPoints } = getArrowPoints(
|
||||
points,
|
||||
size,
|
||||
mode,
|
||||
bezierParameters,
|
||||
end,
|
||||
radians
|
||||
);
|
||||
|
||||
if (rough) {
|
||||
rc.linearPath(arrowPoints as [number, number][], getRcOptions(options));
|
||||
} else {
|
||||
renderRoundedPolygon(ctx, arrowPoints, strokeColor, strokeWidth, false);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderTriangle(
|
||||
points: PointLocation[],
|
||||
ctx: CanvasRenderingContext2D,
|
||||
rc: RoughCanvas,
|
||||
options: ArrowOptions
|
||||
) {
|
||||
const { mode, end, bezierParameters, rough, strokeColor, strokeWidth } =
|
||||
options;
|
||||
const radians = Math.PI / 6;
|
||||
const size = DEFAULT_ARROW_SIZE * (strokeWidth / 2);
|
||||
const { points: trianglePoints } = getArrowPoints(
|
||||
points,
|
||||
size,
|
||||
mode,
|
||||
bezierParameters,
|
||||
end,
|
||||
radians
|
||||
);
|
||||
|
||||
if (rough) {
|
||||
rc.polygon(
|
||||
[
|
||||
[trianglePoints[0][0], trianglePoints[0][1]],
|
||||
[trianglePoints[1][0], trianglePoints[1][1]],
|
||||
[trianglePoints[2][0], trianglePoints[2][1]],
|
||||
],
|
||||
getRcOptions(options)
|
||||
);
|
||||
} else {
|
||||
renderRoundedPolygon(ctx, trianglePoints, strokeColor, strokeWidth);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderDiamond(
|
||||
points: PointLocation[],
|
||||
ctx: CanvasRenderingContext2D,
|
||||
rc: RoughCanvas,
|
||||
options: ArrowOptions
|
||||
) {
|
||||
const { mode, end, rough, bezierParameters, strokeColor, strokeWidth } =
|
||||
options;
|
||||
const anchorPoint = getPointWithTangent(points, mode, end, bezierParameters);
|
||||
const size = 10 * (strokeWidth / 2);
|
||||
const { points: diamondPoints } = getDiamondPoints(anchorPoint, size, end);
|
||||
|
||||
if (rough) {
|
||||
rc.polygon(
|
||||
[
|
||||
[diamondPoints[0][0], diamondPoints[0][1]],
|
||||
[diamondPoints[1][0], diamondPoints[1][1]],
|
||||
[diamondPoints[2][0], diamondPoints[2][1]],
|
||||
[diamondPoints[3][0], diamondPoints[3][1]],
|
||||
],
|
||||
getRcOptions(options)
|
||||
);
|
||||
} else {
|
||||
renderRoundedPolygon(ctx, diamondPoints, strokeColor, strokeWidth);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderCircle(
|
||||
points: PointLocation[],
|
||||
ctx: CanvasRenderingContext2D,
|
||||
rc: RoughCanvas,
|
||||
options: ArrowOptions
|
||||
) {
|
||||
const {
|
||||
bezierParameters,
|
||||
mode,
|
||||
end,
|
||||
fillColor,
|
||||
strokeColor,
|
||||
strokeWidth,
|
||||
rough,
|
||||
} = options;
|
||||
const radius = 5 * (strokeWidth / 2);
|
||||
const centerPoint = getCircleCenterPoint(
|
||||
points,
|
||||
radius,
|
||||
mode,
|
||||
bezierParameters,
|
||||
end
|
||||
);
|
||||
const cx = centerPoint[0];
|
||||
const cy = centerPoint[1];
|
||||
|
||||
if (rough) {
|
||||
// radius + 2 when render rough circle to avoid connector line cross the circle and make it looks bad
|
||||
rc.circle(cx, cy, radius + 2, getRcOptions(options));
|
||||
} else {
|
||||
ctx.fillStyle = fillColor;
|
||||
ctx.strokeStyle = strokeColor;
|
||||
ctx.lineWidth = strokeWidth;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(cx, cy, radius, radius, 0, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './connector-tool';
|
||||
export * from './element-renderer';
|
||||
export * from './element-transform';
|
||||
export * from './text';
|
||||
export * from './toolbar/config';
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
EdgelessCRUDIdentifier,
|
||||
TextUtils,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import { getLineHeight } from '@blocksuite/affine-gfx-text';
|
||||
import type { ConnectorElementModel } from '@blocksuite/affine-model';
|
||||
import type { RichText } from '@blocksuite/affine-rich-text';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
@@ -319,11 +317,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable(
|
||||
labelConstraints: { hasMaxWidth, maxWidth },
|
||||
} = connector;
|
||||
|
||||
const lineHeight = TextUtils.getLineHeight(
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontWeight
|
||||
);
|
||||
const lineHeight = getLineHeight(fontFamily, fontSize, fontWeight);
|
||||
const { translateX, translateY, zoom } = this.gfx.viewport;
|
||||
const [x, y] = Vec.mul(connector.getPointByOffsetDistance(distance), zoom);
|
||||
const transformOperation = [
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import {
|
||||
ConnectorUtils,
|
||||
EdgelessCRUDIdentifier,
|
||||
TextUtils,
|
||||
} from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
packColor,
|
||||
type PickColorEvent,
|
||||
} from '@blocksuite/affine-components/color-picker';
|
||||
import type { LineDetailType } from '@blocksuite/affine-components/edgeless-line-styles-panel';
|
||||
import {
|
||||
createTextActions,
|
||||
normalizeTextBound,
|
||||
} from '@blocksuite/affine-gfx-text';
|
||||
import {
|
||||
ConnectorElementModel,
|
||||
type ConnectorElementProps,
|
||||
@@ -33,7 +36,6 @@ import {
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import type { MenuItem } from '@blocksuite/affine-widget-edgeless-toolbar';
|
||||
import {
|
||||
createTextActions,
|
||||
getRootBlock,
|
||||
LINE_STYLE_LIST,
|
||||
renderMenu,
|
||||
@@ -368,7 +370,7 @@ export const connectorToolbarConfig = {
|
||||
} = model;
|
||||
const prevBounds = Bound.fromXYWH(labelXYWH || [0, 0, 16, 16]);
|
||||
const center = prevBounds.center;
|
||||
const bounds = TextUtils.normalizeTextBound(
|
||||
const bounds = normalizeTextBound(
|
||||
{
|
||||
yText: text!,
|
||||
fontFamily,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"references": [
|
||||
{ "path": "../../blocks/block-surface" },
|
||||
{ "path": "../../components" },
|
||||
{ "path": "../text" },
|
||||
{ "path": "../../model" },
|
||||
{ "path": "../../rich-text" },
|
||||
{ "path": "../../shared" },
|
||||
|
||||
Reference in New Issue
Block a user