Files
AFFiNE-Mirror/blocksuite/affine/model/src/elements/shape/shape.ts
T
2024-12-20 15:38:06 +08:00

278 lines
6.2 KiB
TypeScript

import type {
BaseElementProps,
PointTestOptions,
} from '@blocksuite/block-std/gfx';
import {
field,
GfxLocalElementModel,
GfxPrimitiveElementModel,
local,
prop,
} from '@blocksuite/block-std/gfx';
import type {
Bound,
IBound,
IVec,
PointLocation,
SerializedXYWH,
} from '@blocksuite/global/utils';
import { DocCollection, type Y } from '@blocksuite/store';
import {
type Color,
DEFAULT_ROUGHNESS,
FontFamily,
FontStyle,
FontWeight,
LineColor,
ShapeFillColor,
ShapeStyle,
ShapeTextFontSize,
ShapeType,
StrokeStyle,
TextAlign,
TextResizing,
type TextStyleProps,
TextVerticalAlign,
} from '../../consts/index.js';
import { shapeMethods } from './api/index.js';
export type ShapeProps = BaseElementProps & {
shapeType: ShapeType;
radius: number;
filled: boolean;
fillColor: Color;
strokeWidth: number;
strokeColor: Color;
strokeStyle: StrokeStyle;
shapeStyle: ShapeStyle;
// https://github.com/rough-stuff/rough/wiki#roughness
roughness?: number;
text?: Y.Text;
textHorizontalAlign?: TextAlign;
textVerticalAlign?: TextVerticalAlign;
textResizing?: TextResizing;
maxWidth?: false | number;
} & Partial<TextStyleProps>;
export const SHAPE_TEXT_PADDING = 20;
export const SHAPE_TEXT_VERTICAL_PADDING = 10;
export class ShapeElementModel extends GfxPrimitiveElementModel<ShapeProps> {
/**
* The bound of the text content.
*/
textBound: IBound | null = null;
get type() {
return 'shape';
}
static override propsToY(props: ShapeProps) {
if (props.text && !(props.text instanceof DocCollection.Y.Text)) {
props.text = new DocCollection.Y.Text(props.text);
}
return props;
}
override containsBound(bounds: Bound) {
return shapeMethods[this.shapeType].containsBound(bounds, this);
}
override getLineIntersections(start: IVec, end: IVec) {
return shapeMethods[this.shapeType].getLineIntersections(start, end, this);
}
override getNearestPoint(point: IVec): IVec {
return shapeMethods[this.shapeType].getNearestPoint(point, this) as IVec;
}
override getRelativePointLocation(point: IVec): PointLocation {
return shapeMethods[this.shapeType].getRelativePointLocation(point, this);
}
override includesPoint(x: number, y: number, options: PointTestOptions) {
return shapeMethods[this.shapeType].includesPoint.call(this, x, y, {
...options,
ignoreTransparent: options.ignoreTransparent ?? true,
});
}
@field('#000000' as Color)
accessor color!: Color;
@field()
accessor fillColor: Color = ShapeFillColor.Yellow;
@field()
accessor filled: boolean = false;
@field(FontFamily.Inter as string)
accessor fontFamily!: string;
@field(ShapeTextFontSize.MEDIUM)
accessor fontSize!: number;
@field(FontStyle.Normal as FontStyle)
accessor fontStyle!: FontStyle;
@field(FontWeight.Regular as FontWeight)
accessor fontWeight!: FontWeight;
@field(false as false | number)
accessor maxWidth: false | number = false;
@field([SHAPE_TEXT_VERTICAL_PADDING, SHAPE_TEXT_PADDING])
accessor padding: [number, number] = [
SHAPE_TEXT_VERTICAL_PADDING,
SHAPE_TEXT_PADDING,
];
@field()
accessor radius: number = 0;
@field(0)
accessor rotate: number = 0;
@field(DEFAULT_ROUGHNESS)
accessor roughness: number = DEFAULT_ROUGHNESS;
@field()
accessor shadow: {
/**
* @deprecated Since the shadow blur will reduce the performance of canvas rendering,
* we already disable the shadow blur rendering by default, so set this field will not take effect.
* You can enable it by setting the flag `enable_shape_shadow_blur` in the awareness store.
* https://web.dev/articles/canvas-performance#avoid_shadowblur
*/
blur: number;
offsetX: number;
offsetY: number;
color: string;
} | null = null;
@field()
accessor shapeStyle: ShapeStyle = ShapeStyle.General;
@field()
accessor shapeType: ShapeType = ShapeType.Rect;
@field()
accessor strokeColor: Color = LineColor.Yellow;
@field()
accessor strokeStyle: StrokeStyle = StrokeStyle.Solid;
@field()
accessor strokeWidth: number = 4;
@field()
accessor text: Y.Text | undefined = undefined;
@field(TextAlign.Center as TextAlign)
accessor textAlign!: TextAlign;
@local()
accessor textDisplay: boolean = true;
@field(TextAlign.Center as TextAlign)
accessor textHorizontalAlign!: TextAlign;
@field(TextResizing.AUTO_HEIGHT as TextResizing)
accessor textResizing: TextResizing = TextResizing.AUTO_HEIGHT;
@field(TextVerticalAlign.Center as TextVerticalAlign)
accessor textVerticalAlign!: TextVerticalAlign;
@field()
accessor xywh: SerializedXYWH = '[0,0,100,100]';
}
export class LocalShapeElementModel extends GfxLocalElementModel {
roughness: number = DEFAULT_ROUGHNESS;
textBound: Bound | null = null;
textDisplay: boolean = true;
get type() {
return 'shape';
}
@prop()
accessor color: Color = '#000000';
@prop()
accessor fillColor: Color = ShapeFillColor.Yellow;
@prop()
accessor filled: boolean = false;
@prop()
accessor fontFamily: string = FontFamily.Inter;
@prop()
accessor fontSize: number = 16;
@prop()
accessor fontStyle: FontStyle = FontStyle.Normal;
@prop()
accessor fontWeight: FontWeight = FontWeight.Regular;
@prop()
accessor padding: [number, number] = [
SHAPE_TEXT_VERTICAL_PADDING,
SHAPE_TEXT_PADDING,
];
@prop()
accessor radius: number = 0;
@prop()
accessor shadow: {
blur: number;
offsetX: number;
offsetY: number;
color: string;
} | null = null;
@prop()
accessor shapeStyle: ShapeStyle = ShapeStyle.General;
@prop()
accessor shapeType: ShapeType = ShapeType.Rect;
@prop()
accessor strokeColor: Color = LineColor.Yellow;
@prop()
accessor strokeStyle: StrokeStyle = StrokeStyle.Solid;
@prop()
accessor strokeWidth: number = 4;
@prop()
accessor text: string = '';
@prop()
accessor textAlign: TextAlign = TextAlign.Center;
@prop()
accessor textVerticalAlign: TextVerticalAlign = TextVerticalAlign.Center;
}
declare global {
namespace BlockSuite {
interface SurfaceElementModelMap {
shape: ShapeElementModel;
}
interface EdgelessTextModelMap {
shape: ShapeElementModel;
}
}
}