npm package: @knotx/decorators
Description: Decorators for Knotx
Author: boenfu
Latest version: 0.5.10

# @knotx/decorators

一个为 Knotx 图形编辑器提供装饰器支持的 TypeScript 包，用于简化插件开发过程。

## 安装

```bash
npm install @knotx/decorators
```

或者使用 yarn：

```bash
yarn add @knotx/decorators
```

或者使用 pnpm：

```bash
pnpm add @knotx/decorators
```

## 基本概述

`@knotx/decorators` 提供了一套完整的 TypeScript 装饰器，帮助开发者更高效地创建 Knotx 插件。这些装饰器封装了常见的插件开发模式，包括节点渲染、边渲染、数据管理、生命周期管理等功能。

## 主要功能

- **节点管理**：提供节点类型定义、操作和管道处理
- **边管理**：提供边类型定义、操作和管道处理
- **插件管理**：提供配置管理、生命周期控制和工具函数注册
- **数据管理**：提供响应式数据绑定和依赖注入
- **UI 管理**：提供层级管理和面板渲染

## API 文档

### 节点相关装饰器

#### @nodeType

用于声明一个方法作为节点渲染器。

```tsx
import { nodeType } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @nodeType('my-node')
  renderNode(node: Node) {
    return (
      <div className="my-node">
        <h3>{node.data.title}</h3>
        <p>{node.data.content}</p>
      </div>
    )
  }
}
```

**参数**：
- `type: string` - 节点类型标识符

**返回值**：返回一个 React 组件或 JSX 元素

#### @nodeOperator

用于声明一个方法作为节点操作函数。

```typescript
import { nodeOperator } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @nodeOperator()
  addNode(x: number, y: number) {
    return [{
      type: 'add',
      node: {
        id: generateId(),
        type: 'default',
        position: { x, y },
        data: { title: 'New Node' }
      }
    }]
  }

  @nodeOperator()
  deleteNode(nodeId: string) {
    return [{
      type: 'remove',
      nodeId
    }]
  }
}
```

**使用说明**：
- 方法必须返回一个操作数组
- 操作会自动分发到引擎进行处理
- 支持批量操作

#### @nodePipe

用于声明一个方法作为节点操作管道，在节点操作执行前后进行处理。

```typescript
import { nodePipe } from '@knotx/decorators'
import { filter, map } from 'rxjs/operators'

class MyPlugin extends BasePlugin {
  @nodePipe()
  validateNodeOperation() {
    return {
      preOperation: () => filter((operation) => {
        if (operation.type === 'add') {
          return this.validateNode(operation.node)
        }
        return true
      }),
      postOperation: () => map((operation) => {
        console.log('Operation completed:', operation)
        return operation
      })
    }
  }
}
```

**返回值**：返回一个包含 `preOperation` 和 `postOperation` 的对象

### 边相关装饰器

#### @edgeType

用于声明一个方法作为边渲染器。

```tsx
import { edgeType } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @edgeType('my-edge', {
    sourcePosition: 'right',
    targetPosition: 'left',
    sourceXOffset: 10,
    targetXOffset: -10
  })
  renderEdge(edge: Edge) {
    return (
      <g>
        <path
          d={edge.path}
          stroke="#333"
          strokeWidth={2}
          fill="none"
        />
        <text x={edge.labelX} y={edge.labelY}>
          {edge.label}
        </text>
      </g>
    )
  }
}
```

**参数**：
- `type: string` - 边类型标识符
- `edgeConfig?: EdgeConfig` - 边配置选项
  - `sourcePosition: Position` - 源连接点位置
  - `targetPosition: Position` - 目标连接点位置
  - `sourceXOffset: number` - 源连接点 X 偏移
  - `sourceYOffset: number` - 源连接点 Y 偏移
  - `targetXOffset: number` - 目标连接点 X 偏移
  - `targetYOffset: number` - 目标连接点 Y 偏移

#### @edgeOperator

用于声明一个方法作为边操作函数。

```typescript
import { edgeOperator } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @edgeOperator()
  addEdge(sourceId: string, targetId: string) {
    return [{
      type: 'add',
      edge: {
        id: generateId(),
        source: sourceId,
        target: targetId,
        type: 'default'
      }
    }]
  }
}
```

#### @edgePipe

用于声明一个方法作为边操作管道。

```typescript
import { edgePipe } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @edgePipe()
  validateEdgeOperation() {
    return {
      preOperation: () => filter((operation) => {
        if (operation.type === 'add') {
          return this.validateEdge(operation.edge)
        }
        return true
      })
    }
  }
}
```

### 插件相关装饰器

#### @config

自动绑定配置管理和生命周期函数。

```typescript
import { config } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @config()
  config = {
    theme: 'light',
    showGrid: true,
    gridSize: 20
  }

  // 配置会自动在 onInit 和 onConfigChange 时更新
}
```

#### @register

用于将插件的属性注册到引擎中，使其他插件可以通过 inject 访问。

```typescript
import { register } from '@knotx/decorators'

class HistoryPlugin extends BasePlugin {
  @register('canUndo')
  canUndo = false

  @register('canRedo')
  canRedo = false

  @register('history')
  get history() {
    return this.historyStack
  }
}
```

#### @tool

用于装饰插件中的工具函数，使其能被引擎和其他插件调用。

```typescript
import { tool } from '@knotx/decorators'

class MathPlugin extends BasePlugin {
  @tool('计算两点距离', {
    type: 'object',
    properties: {
      x1: { type: 'number', description: '第一个点的 X 坐标' },
      y1: { type: 'number', description: '第一个点的 Y 坐标' },
      x2: { type: 'number', description: '第二个点的 X 坐标' },
      y2: { type: 'number', description: '第二个点的 Y 坐标' }
    },
    required: ['x1', 'y1', 'x2', 'y2']
  })
  calculateDistance(params: { x1: number, y1: number, x2: number, y2: number }) {
    const dx = params.x2 - params.x1
    const dy = params.y2 - params.y1
    return Math.sqrt(dx * dx + dy * dy)
  }
}
```

**参数**：
- `description: string` - 工具函数描述
- `parameters: Schema` - 参数的 JSON Schema 定义

#### @observable

创建响应式属性，支持数据绑定和变更监听。

```typescript
import { observable } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @observable()
  selectedNodes: string[] = []

  @observable()
  zoomLevel = 1

  updateSelection(nodeIds: string[]) {
    this.selectedNodes = nodeIds // 自动触发变更通知
  }
}
```

#### 生命周期装饰器

##### @OnInit

在插件初始化时调用。

```typescript
import { OnInit } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @OnInit
  initialize(config: PluginConfig) {
    console.log('Plugin initialized with config:', config)
    this.setupEventListeners()
  }
}
```

##### @OnDestroy

在插件销毁时调用。

```typescript
import { OnDestroy } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @OnDestroy
  cleanup() {
    console.log('Plugin destroyed')
    this.clearEventListeners()
  }
}
```

##### @OnConfigChange

在插件配置变更时调用。

```typescript
import { OnConfigChange } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @OnConfigChange
  handleConfigChange(newConfig: PluginConfig) {
    console.log('Config changed:', newConfig)
    this.applyNewConfig(newConfig)
  }
}
```

### 引擎相关装饰器

#### @inject

用于注入引擎变量或其他插件的数据。

```typescript
import { inject } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  // 注入引擎变量
  @inject.nodes()
  nodes: Node[] = []

  // 注入引擎方法
  @inject.getNodeById()
  getNodeById!: (id: string) => Node | undefined

  // 注入其他插件的数据
  @inject.history.canUndo()
  canUndo = false

  // 使用选择器注入特定数据
  @inject.nodes((nodes: Node[]) => nodes.filter(n => n.selected))
  selectedNodes: Node[] = []
}
```

**使用方式**：
- `@inject.property()` - 注入引擎变量
- `@inject.property(selector)` - 注入引擎变量并使用选择器
- `@inject.pluginName.property()` - 注入插件变量
- `@inject.methodName()` - 注入引擎方法

#### @subscribe

用于订阅引擎变量或其他插件的数据变化。

```typescript
import { subscribe } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  // 订阅引擎变量
  @subscribe.nodes()
  handleNodesChange(nodes: Node[]) {
    console.log('Nodes changed:', nodes)
    this.updateUI(nodes)
  }

  // 订阅其他插件的数据
  @subscribe.history.canUndo()
  handleUndoStateChange(canUndo: boolean) {
    this.updateUndoButton(canUndo)
  }
}
```

### 层相关装饰器

#### @layer

用于声明方法所属的渲染层级。

```tsx
import { Layer } from '@knotx/core'
import { layer } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @layer(Layer.BACKGROUND)
  renderBackground() {
    return (
      <rect width="100%" height="100%" fill="#f0f0f0" />
    )
  }

  @layer(Layer.FOREGROUND, 100)
  renderOverlay() {
    return (
      <div className="overlay">
        Overlay content
      </div>
    )
  }
}
```

**参数**：
- `layer: Layer` - 层级枚举值
- `offset?: number` - 层级偏移值

#### @panel

用于创建面板组件。

```tsx
import { panel } from '@knotx/decorators'

class MyPlugin extends BasePlugin {
  @panel('top', { x: 10, y: 10 })
  renderToolbar() {
    return (
      <div className="toolbar">
        <button>Save</button>
        <button>Load</button>
        <button>Export</button>
      </div>
    )
  }

  @panel('bottom-right')
  renderStatus() {
    return (
      <div className="status">
        Ready
      </div>
    )
  }
}
```

**参数**：
- `position: Position` - 面板位置（'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'）
- `offset?: PanelOffset` - 面板偏移
  - `x?: number` - X 轴偏移
  - `y?: number` - Y 轴偏移

## 文件目录结构

```
src/
├── index.ts                 # 主导出文件
├── internal.ts             # 内部工具函数
├── edge/                   # 边相关装饰器
│   ├── index.ts           # 边模块导出
│   ├── edgeType.ts        # 边类型装饰器
│   ├── edgeOperator.ts    # 边操作装饰器
│   └── edgePipe.ts        # 边管道装饰器
├── engine/                 # 引擎相关装饰器
│   ├── index.ts           # 引擎模块导出
│   ├── inject/            # 注入装饰器
│   │   ├── index.ts       # 注入模块导出
│   │   ├── inject-data.ts # 数据注入实现
│   │   ├── inject-decorator.ts # 注入装饰器实现
│   │   ├── inject-method.ts # 方法注入实现
│   │   ├── inject-plugin.ts # 插件注入实现
│   │   ├── injectable.ts  # 可注入接口
│   │   ├── injectable-proxy.ts # 注入代理实现
│   │   ├── proxy.ts       # 代理对象
│   │   └── types.ts       # 类型定义
│   └── subscribe/         # 订阅装饰器
│       ├── index.ts       # 订阅模块导出
│       ├── proxy.ts       # 订阅代理对象
│       ├── subscribe-data.ts # 数据订阅实现
│       ├── subscribe-decorator.ts # 订阅装饰器实现
│       ├── subscribe-plugin.ts # 插件订阅实现
│       └── types.ts       # 类型定义
├── layer/                  # 层相关装饰器
│   ├── index.ts           # 层模块导出
│   ├── layer.ts           # 层装饰器
│   └── panel.tsx          # 面板装饰器
├── node/                   # 节点相关装饰器
│   ├── index.ts           # 节点模块导出
│   ├── nodeType.ts        # 节点类型装饰器
│   ├── nodeOperator.ts    # 节点操作装饰器
│   └── nodePipe.ts        # 节点管道装饰器
└── plugin/                 # 插件相关装饰器
    ├── index.ts           # 插件模块导出
    ├── config.ts          # 配置装饰器
    ├── lifecycle.ts       # 生命周期装饰器
    ├── observable.ts      # 响应式装饰器
    ├── register.ts        # 注册装饰器
    └── tool.ts            # 工具函数装饰器
```

## 依赖关系

### 对等依赖 (Peer Dependencies)

- `@knotx/jsx` - Knotx JSX 运行时支持

### 主要依赖 (Dependencies)

- `@knotx/core` - Knotx 核心库
- `jsonschema` - JSON Schema 验证库
- `lodash-es` - 实用工具函数库
- `rxjs` - 响应式编程库

## 许可证

MIT License

## 贡献指南

欢迎提交 Issue 和 Pull Request。在提交代码前，请确保：

1. 代码符合项目的 ESLint 规则
2. 通过 TypeScript 类型检查
3. 添加必要的测试用例
4. 更新相关文档

## 更多信息

- [Knotx 官网](https://github.com/boenfu/knotx)
- [API 文档](https://github.com/boenfu/knotx#readme)
- [示例项目](https://github.com/boenfu/knotx/tree/main/apps/playground)