Skip to content

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 快照。

MIT License