npm package: @knotx/render
Description: Render for Knotx
Author: boenfu
Latest version: 0.5.10

# @knotx/render

一个专为 Knotx 图形编辑器提供边缘（Edge）渲染功能的 React 组件库。

## 📦 安装

```bash
npm install @knotx/render
```

```bash
yarn add @knotx/render
```

```bash
pnpm add @knotx/render
```

## 🚀 基本概述

`@knotx/render` 是 Knotx 生态系统中的渲染层组件库，专门用于渲染图形编辑器中的边缘（连接线）。它提供了多种边缘类型（直线、贝塞尔曲线）和丰富的自定义选项，支持箭头标记、标签、样式定制等功能。

### 主要特性

- 🎨 **多种边缘类型**: 支持直线、贝塞尔曲线等多种连接线类型
- 🎯 **箭头标记**: 内置多种箭头标记样式，支持自定义标记
- 🏷️ **标签支持**: 支持在边缘上添加文本标签
- 🎛️ **高度可定制**: 支持样式、颜色、宽度等各种属性自定义
- 🔧 **扩展性强**: 基于抽象路径构建器设计，可轻松扩展新的边缘类型
- ⚡ **性能优化**: 采用 SVG 渲染，支持交互区域优化

## 📋 API 文档

### 组件

#### `BaseEdge<TPathBuilderContext>`

基础边缘组件，所有其他边缘组件的基础。

**Props**

```tsx
interface BaseEdgeProps<TPathBuilderContext = void> extends EdgeProps {
  className?: string // 自定义 CSS 类名
  style?: Record<string, any> // 自定义样式对象
  interactionWidth?: number // 交互区域宽度，默认 20
  label?: string // 边缘标签文本
  markerEnd?: string | MarkerProps // 终点标记
  markerStart?: string | MarkerProps // 起点标记
  pathBuilder: PathBuilderType<any, TPathBuilderContext> // 路径构建器
  context: TPathBuilderContext // 路径构建器上下文
}
```

**使用示例**

```tsx
import { BaseEdge, StraightPathBuilder } from '@knotx/render'

function CustomEdge() {
  return (
    <BaseEdge
      sourceX={0}
      sourceY={0}
      targetX={100}
      targetY={100}
      pathBuilder={StraightPathBuilder}
      context={undefined}
      label="连接线"
      style={{ stroke: '#1890ff', strokeWidth: 2 }}
    />
  )
}
```

#### `BezierEdge`

贝塞尔曲线边缘组件，适用于需要平滑曲线连接的场景。

**Props**

```tsx
interface BezierEdgeProps extends Omit<BaseEdgeProps, 'pathBuilder' | 'context'> {}
```

**使用示例**

```tsx
import { BezierEdge, getArrowMarker } from '@knotx/render'

function CurvedConnection() {
  const arrowMarker = getArrowMarker('#ff4d4f')

  return (
    <BezierEdge
      sourceX={50}
      sourceY={50}
      targetX={250}
      targetY={150}
      markerEnd={arrowMarker}
      style={{ stroke: '#ff4d4f', strokeWidth: 2 }}
      label="贝塞尔曲线"
    />
  )
}
```

#### `StraightEdge`

直线边缘组件，适用于简单的直线连接。

**Props**

```tsx
interface StraightEdgeProps extends Omit<BaseEdgeProps, 'pathBuilder' | 'context'> {}
```

**使用示例**

```tsx
import { getArrowClosedMarker, StraightEdge } from '@knotx/render'

function DirectConnection() {
  const arrowMarker = getArrowClosedMarker('#52c41a')

  return (
    <StraightEdge
      sourceX={0}
      sourceY={0}
      targetX={200}
      targetY={100}
      markerEnd={arrowMarker}
      markerStart={arrowMarker}
      style={{ stroke: '#52c41a', strokeWidth: 3 }}
      interactionWidth={30}
    />
  )
}
```

#### `MarkerDefinitions`

标记定义组件，用于集中管理和重用标记。

**Props**

```typescript
interface MarkerDefinitionsProps {
  markers: MarkerProps[]
}
```

**使用示例**

```tsx
import { getArrowClosedMarker, getArrowMarker, MarkerDefinitions } from '@knotx/render'

function EdgeContainer() {
  const markers = [
    getArrowMarker('#1890ff'),
    getArrowClosedMarker('#ff4d4f'),
    getArrowMarker('#52c41a')
  ]

  return (
    <svg>
      <MarkerDefinitions markers={markers} />
      {/* 你的边缘组件 */}
    </svg>
  )
}
```

### 路径构建器

#### `PathBuilder<TType, TContext>`

抽象路径构建器基类，用于创建自定义边缘类型。

**抽象方法**

```tsx
abstract class PathBuilder<TType extends string, TContext = never> {
  constructor(
    readonly sourceX: number,
    readonly sourceY: number,
    readonly targetX: number,
    readonly targetY: number
  ) {}

  abstract type: TType
  abstract buildPath(context: TContext): string
  abstract interpolate(t: number, context: TContext): { x: number, y: number }
}
```

**创建自定义路径构建器示例**

```tsx
class CustomPathBuilder extends PathBuilder<'custom'> {
  type = 'custom' as const

  buildPath(): string {
    // 创建自定义的 SVG 路径
    const midX = (this.sourceX + this.targetX) / 2
    const midY = (this.sourceY + this.targetY) / 2

    return `M ${this.sourceX} ${this.sourceY}
            Q ${midX} ${midY - 50} ${this.targetX} ${this.targetY}`
  }

  interpolate(t: number): { x: number, y: number } {
    // 实现路径上点的插值计算
    const x = this.sourceX + t * (this.targetX - this.sourceX)
    const y = this.sourceY + t * (this.targetY - this.sourceY)
    return { x, y }
  }
}
```

#### `StraightPathBuilder`

直线路径构建器，生成直线路径。

```tsx
class StraightPathBuilder extends PathBuilder<'straight'> {
  type = 'straight' as const
  buildPath(): string
  interpolate(t: number): { x: number, y: number }
}
```

#### `BezierPathBuilder`

贝塞尔曲线路径构建器，生成平滑的曲线路径。

```tsx
class BezierPathBuilder extends PathBuilder<'bezier'> {
  type = 'bezier' as const
  buildPath(): string
  interpolate(t: number): { x: number, y: number }
}
```

### 标记工具函数

#### `getArrowMarker(color?: string)`

创建标准箭头标记。

**参数**
- `color` (可选): 标记颜色，默认为 `currentColor`

**返回值**
- `MarkerProps`: 箭头标记配置对象

**使用示例**

```tsx
import { getArrowMarker, StraightEdge } from '@knotx/render'

function EdgeWithArrow() {
  const marker = getArrowMarker('#1890ff')

  return (
    <StraightEdge
      sourceX={0}
      sourceY={0}
      targetX={100}
      targetY={100}
      markerEnd={marker}
    />
  )
}
```

#### `getArrowClosedMarker(color?: string)`

创建实心箭头标记。

**参数**
- `color` (可选): 标记颜色，默认为 `currentColor`

**返回值**
- `MarkerProps`: 实心箭头标记配置对象

**使用示例**

```tsx
import { BezierEdge, getArrowClosedMarker } from '@knotx/render'

function EdgeWithClosedArrow() {
  const marker = getArrowClosedMarker('#ff4d4f')

  return (
    <BezierEdge
      sourceX={50}
      sourceY={50}
      targetX={200}
      targetY={150}
      markerEnd={marker}
      style={{ stroke: '#ff4d4f' }}
    />
  )
}
```

#### `getCustomMarker(type: string, options?: Partial<MarkerProps>)`

创建自定义标记。

**参数**
- `type`: 标记类型标识符
- `options` (可选): 自定义标记选项

**返回值**
- `MarkerProps`: 自定义标记配置对象

**使用示例**

```tsx
import { BaseEdge, getCustomMarker, StraightPathBuilder } from '@knotx/render'

function EdgeWithCustomMarker() {
  const marker = getCustomMarker('diamond', {
    color: '#722ed1',
    width: 12,
    height: 12,
    strokeWidth: 2
  })

  return (
    <BaseEdge
      sourceX={0}
      sourceY={0}
      targetX={150}
      targetY={100}
      pathBuilder={StraightPathBuilder}
      context={undefined}
      markerEnd={marker}
    />
  )
}
```

#### `getArrowHeadMarkers(color?: string)`

创建两个实心箭头作为起点和终点的标记。

**参数**
- `color` (可选): 标记颜色

**返回值**
- 包含 `markerStart` 和 `markerEnd` 的对象

**使用示例**

```tsx
import { getArrowHeadMarkers, StraightEdge } from '@knotx/render'

function DoubleArrowEdge() {
  const { markerStart, markerEnd } = getArrowHeadMarkers('#13c2c2')

  return (
    <StraightEdge
      sourceX={0}
      sourceY={0}
      targetX={200}
      targetY={100}
      markerStart={markerStart}
      markerEnd={markerEnd}
      style={{ stroke: '#13c2c2', strokeWidth: 2 }}
    />
  )
}
```

### 类型定义

#### `MarkerType`

```typescript
enum MarkerType {
  Arrow = 'arrow',
  ArrowClosed = 'arrowclosed'
}
```

#### `MarkerProps`

```typescript
interface MarkerProps {
  id: string // 标记唯一标识符
  type: MarkerType | string // 标记类型
  color?: string // 标记颜色
  width?: number // 标记宽度
  height?: number // 标记高度
  markerUnits?: string // 标记单位
  orient?: string // 标记方向
  strokeWidth?: number // 描边宽度
}
```

#### `PathBuilderType`

```tsx
type PathBuilderType<T extends string = any, TContext = void> =
  new (...args: ConstructorParameters<typeof PathBuilder<T, TContext>>) =>
  PathBuilder<T, TContext>
```

## 🗂️ 文件目录结构

```
packages/render/
├── src/
│   ├── edges/
│   │   ├── base-edge.tsx        # 基础边缘组件
│   │   ├── bezier.tsx           # 贝塞尔曲线边缘
│   │   ├── straight.tsx         # 直线边缘
│   │   ├── markers.tsx          # 标记相关功能
│   │   ├── path-builder.ts      # 路径构建器
│   │   └── index.ts             # 边缘模块导出
│   └── index.ts                 # 主入口文件
├── dist/                        # 构建输出目录
├── package.json                 # 包配置文件
├── tsconfig.json               # TypeScript 配置
├── build.config.ts             # 构建配置
└── README.md                   # 文档文件
```

## 🎯 完整使用示例

### 基础使用

```tsx
import {
  BezierEdge,
  getArrowClosedMarker,
  getArrowMarker,
  StraightEdge
} from '@knotx/render'
import React from 'react'

function GraphEditor() {
  return (
    <div style={{ position: 'relative', width: 800, height: 600 }}>
      {/* 直线连接 */}
      <StraightEdge
        sourceX={50}
        sourceY={50}
        targetX={200}
        targetY={100}
        markerEnd={getArrowMarker('#1890ff')}
        style={{ stroke: '#1890ff', strokeWidth: 2 }}
        label="直线连接"
      />

      {/* 贝塞尔曲线连接 */}
      <BezierEdge
        sourceX={50}
        sourceY={150}
        targetX={250}
        targetY={200}
        markerEnd={getArrowClosedMarker('#ff4d4f')}
        style={{ stroke: '#ff4d4f', strokeWidth: 2 }}
        label="曲线连接"
      />
    </div>
  )
}
```

### 自定义边缘类型

```tsx
import { BaseEdge, PathBuilder } from '@knotx/render'

// 创建自定义路径构建器
class StepPathBuilder extends PathBuilder<'step'> {
  type = 'step' as const

  buildPath(): string {
    const midX = (this.sourceX + this.targetX) / 2
    return `M ${this.sourceX} ${this.sourceY}
            L ${midX} ${this.sourceY}
            L ${midX} ${this.targetY}
            L ${this.targetX} ${this.targetY}`
  }

  interpolate(t: number): { x: number, y: number } {
    const midX = (this.sourceX + this.targetX) / 2

    if (t <= 0.33) {
      const localT = t / 0.33
      return {
        x: this.sourceX + localT * (midX - this.sourceX),
        y: this.sourceY
      }
    }
    else if (t <= 0.66) {
      const localT = (t - 0.33) / 0.33
      return {
        x: midX,
        y: this.sourceY + localT * (this.targetY - this.sourceY)
      }
    }
    else {
      const localT = (t - 0.66) / 0.34
      return {
        x: midX + localT * (this.targetX - midX),
        y: this.targetY
      }
    }
  }
}

// 使用自定义路径构建器
function StepEdge(props: Omit<BaseEdgeProps, 'pathBuilder' | 'context'>) {
  return (
    <BaseEdge
      {...props}
      pathBuilder={StepPathBuilder}
      context={undefined}
    />
  )
}
```

### 高级标记管理

```tsx
import { getArrowMarker, getCustomMarker, MarkerDefinitions } from '@knotx/render'

function AdvancedGraph() {
  // 预定义标记
  const markers = [
    getArrowMarker('#1890ff'),
    getArrowMarker('#ff4d4f'),
    getCustomMarker('circle', {
      color: '#52c41a',
      width: 8,
      height: 8
    })
  ]

  return (
    <svg width="800" height="600">
      <MarkerDefinitions markers={markers} />

      {/* 使用预定义标记 */}
      <StraightEdge
        sourceX={100}
        sourceY={100}
        targetX={300}
        targetY={200}
        markerEnd={markers[0]}
        style={{ stroke: '#1890ff' }}
      />

      <BezierEdge
        sourceX={100}
        sourceY={200}
        targetX={400}
        targetY={150}
        markerEnd={markers[1]}
        style={{ stroke: '#ff4d4f' }}
      />
    </svg>
  )
}
```

## 📄 许可证

MIT

## 🤝 贡献

欢迎提交问题和拉取请求。详见 [CONTRIBUTING.md](../../CONTRIBUTING.md)

## 📞 支持

如果您遇到任何问题或需要帮助，请在 [GitHub Issues](https://github.com/boenfu/knotx/issues) 中提交问题。