主题
registry · 注册表
形状、边和 marker 都是注册项。注册表不可变:每次扩展返回新实例,实例本身就是缓存版本。
签名
ts
function createRegistry(
entries?: RegistryEntries | { base?: DrawRegistry },
): DrawRegistry;
function emptyRegistry(): DrawRegistry;
function defineShape<P>(spec: ShapeSpec<P>): ShapeSpec<P>;
function defineEdge(spec: EdgeSpec): EdgeSpec;
interface DrawRegistry {
readonly version: number;
with(entries: RegistryEntries, options?: { override?: boolean }): DrawRegistry;
shape(type: string): ShapeSpec | undefined;
edge(type: string): EdgeSpec | undefined;
marker(type: string): MarkerSpec | undefined;
types(): RegistryTypes;
metrics(): ShapeMetrics;
}同名注册默认抛 RegistryConflictError。配置期错误不折成逐帧诊断;确实要替换时显式传 { override: true }。
默认注册表
createRegistry()自带内置形状和 marker;emptyRegistry()完全空白;createRegistry({ base })复制指定实例,兼容 v1 写法;registerShape / registerEdge / registerMarker作为 v1 别名保留,但同样返回新实例,不再原地修改。
形状契约
ts
interface ShapeEntry<P = unknown> {
schema?: PropsSchema<P>;
container?: {
header(props: P, label: Label | undefined, typography: Typography): number;
};
measure(props: P, label: Label | undefined, typography: Typography, measurer: TextMeasurer): Size;
anchors?(port: Port, props: P, box: Rect): Point;
}
interface ShapeSpec<P = unknown> extends ShapeEntry<P> {
render(ctx: ShapeRenderCtx<P>): void;
}PropsSchema 接受同步 safeParse 或同步 Standard Schema。bind 只解析一次,layout 与 build 共享归一化后的 props。
container 决定节点是否能拥有 children。标题带高度由形状自己解释,layout 不再替所有类型猜。
边契约
ts
interface EdgeSpec {
schema?: PropsSchema;
markers?: { start?: string; end?: string };
labels?: "auto" | "manual";
render(ctx: EdgeRenderCtx): void;
}labels 缺省为 auto:measure 量标签,route 给落点,build 统一绘制。 需要自己排版时写 manual,并使用 ctx.labelOut。
render 入参
ts
interface ShapeRenderCtx<P = unknown> {
key: string;
box: Rect;
anchors: Record<string, Point>;
props: P;
label: Label | undefined;
style: Style;
typography: Typography;
measure: MeasureFn;
out: SceneSink;
}
interface EdgeRenderCtx {
edgeId: string;
route: Route;
source: Point;
target: Point;
props: unknown;
label: Label | undefined;
labelPlacement?: LabelPlacement;
style: Style;
typography: Typography;
measure: MeasureFn;
marker(type: string): MarkerSpec | undefined;
out: SceneSink;
labelOut: SceneSink;
}render 拿不到整份文档、布局结果或其他元素。跨元素几何不能从 render 泄漏出去。
SceneSink
ts
interface SceneSink {
key(suffix: string): string;
node(node: SceneNode): void;
group(
suffix: string,
build: (inner: SceneSink) => void,
options?: { clip?: boolean; style?: Style },
): void;
child(suffix: string, type: string, props: unknown, box: Rect, options?: ChildOptions): void;
warn(d: Diagnostic): void;
}child 用另一个注册项画复合零件。稳定 key 是增量更新的基础,不要使用随机数或数组下标。
内置原语
| 类型 | 形态 |
|---|---|
rect | 圆角盒子 + 标签 |
ellipse | 椭圆 + 标签 |
path | 绝对路径 + 标签 |
text | 纯文字 |
group | 容器背景 + 标题带 |
内置 marker:arrow、open-arrow、triangle、dot、diamond。 库内没有内置边类型,边型属于上层语义。
metrics()
metrics() 是 v1 兼容视图,只暴露 measure / anchors / schema / container,不暴露 render。 新代码应直接把 BoundDocument 传给 layout,不需要自行管理 ShapeMetrics 快照。