npm package: @knotx/core
Description: Core for Knotx
Author: boenfu
Latest version: 0.5.10

# @knotx/core

[English](./README.en.md) | **中文**

---

## 📦 安装

```bash
npm install @knotx/core
```

```bash
yarn add @knotx/core
```

```bash
pnpm add @knotx/core
```

## 📖 概述

`@knotx/core` 是 Knotx 图形编辑器的核心包，提供了构建可视化图形编辑器所需的基础架构。它包含引擎管理、插件系统、交互管理、元素操作、层级渲染等核心功能，为开发者提供了强大而灵活的图形编辑能力。

## 🚀 快速开始

```typescript
import { Engine } from '@knotx/core'

// 创建引擎实例
const engine = new Engine({
  container: { width: 800, height: 600, direction: 'horizontal' },
  nodes: [
    { id: '1', position: { x: 100, y: 100 }, data: { label: 'Node 1' } },
    { id: '2', position: { x: 300, y: 200 }, data: { label: 'Node 2' } }
  ],
  edges: [
    { id: 'e1', source: '1', target: '2', data: {} }
  ]
})

// 获取节点
const nodes = engine.getNodes()
console.log(nodes)
```

## 📚 完整 API 文档

### 🔧 核心类

#### Engine 类

引擎是 Knotx 的核心，负责管理整个图形编辑器的状态和行为。

```typescript
// Engine 类的类型定义
interface Engine<TRenderType extends RenderType = RenderType, TNode extends IRecord = IRecord, TEdge extends IRecord = IRecord> {
  // 引擎的方法和属性
}
```

**属性：**
- `runtime: IEngineRuntime` - 运行时配置
- `interaction: InteractionManager` - 交互管理器
- `nodesManager: DataManager<Node<TNode>>` - 节点数据管理器
- `edgesManager: DataManager<Edge<TEdge>>` - 边数据管理器
- `container: Container` - 容器配置
- `nodes: Node<TNode>[]` - 节点数组
- `edges: Edge<TEdge>[]` - 边数组
- `layers: Map<number, LayerComponent<TRenderType>[]>` - 层级组件映射
- `plugins: IPlugin[]` - 插件数组

**方法：**
- `getNodes(): Node<TNode>[]` - 获取所有节点
- `getEdges(): Edge<TEdge>[]` - 获取所有边
- `getNode({ id }): Node<TNode> | undefined` - 根据 ID 获取节点
- `getEdge({ id }): Edge<TEdge> | undefined` - 根据 ID 获取边
- `getNodeDraft({ id }): Node<TNode> | undefined` - 获取节点草稿
- `getEdgeDraft({ id }): Edge<TEdge> | undefined` - 获取边草稿
- `getPlugin<TPlugin>({ pluginName }): TPlugin | undefined` - 获取插件
- `getNodeRenderer(type: string): NodeRenderType | undefined` - 获取节点渲染器
- `getEdgeRenderer(type: string): EdgeRender | undefined` - 获取边渲染器
- `getLayerComponents(layer: Layer): LayerComponent<TRenderType>[]` - 获取层级组件
- `addNodePipe(pipe: DataOperationPipe<Node<TNode>>)` - 添加节点操作管道
- `addEdgePipe(pipe: DataOperationPipe<Edge<TEdge>>)` - 添加边操作管道
- `dispatchNodeOperation(operation: DataOperation<Node<TNode>>)` - 派发节点操作
- `dispatchEdgeOperation(operation: DataOperation<Edge<TEdge>>)` - 派发边操作
- `registerNodeRenderer(type: string, renderer: NodeRenderType): () => void` - 注册节点渲染器
- `registerEdgeRenderer(type: string, renderer: EdgeRenderType, config: Required<EdgeConfig>): () => void` - 注册边渲染器
- `registerPluginData<T, TP>(pluginName: T, property: TP, data: BehaviorSubject<PluginData[T][TP]>): void` - 注册插件数据
- `subscribePluginData<T, TP>(pluginName: T, property: TP, observer: (value: PluginData[T][TP]) => void): Subscription | null` - 订阅插件数据
- `registerPluginTool<T, TP>(pluginName: T, property: TP, data: { description: string; parameters: Schema; func: PluginTools[T][TP] }): void` - 注册插件工具
- `resetPluginData<T>(pluginName: T): void` - 重置插件数据
- `resetPluginTool<T>(pluginName: T): void` - 重置插件工具
- `changePluginConfig({ pluginName, config }): void` - 更改插件配置
- `callTool: CallToolMethod` - 调用工具方法
- `listPlugins(): IPluginInfo[]` - 列出所有插件
- `listPluginTools({ pluginName }): IToolInfo[]` - 列出插件工具
- `listEngineTools(): IToolInfo[]` - 列出引擎工具
- `destroy()` - 销毁引擎

#### BasePlugin 类

插件基类，提供插件开发的基础功能。

```typescript
// BasePlugin 抽象类的类型定义
abstract class BasePlugin<TPluginName extends string = string, TPluginConfig extends IRecord | undefined = undefined, TRenderType extends RenderType = RenderType> {
  // 插件的抽象方法和属性
}
```

**属性：**
- `abstract name: TPluginName` - 插件名称
- `pluginId: string` - 插件唯一ID
- `protected subscriptions: (SubscriptionLike | (() => void))[]` - 订阅集合
- `protected callTool: Engine['callTool']` - 调用工具方法

**方法：**
- `onInit?(config: TPluginConfig): void` - 初始化回调
- `onConfigChange?(config: TPluginConfig): void` - 配置变更回调
- `onDestroy?(): void` - 销毁回调

#### InteractionManager 类

交互管理器，负责管理用户交互的优先级和状态。

```typescript
// InteractionManager 类的类型定义
class InteractionManager {
  // 交互管理器的方法和属性
}
```

**属性：**
- `active: Interaction | undefined` - 当前活动交互

**方法：**
- `canInteract(priority: InteractionPriority): boolean` - 检查是否可以交互
- `start(pluginId: string, type: string, priority: InteractionPriority): () => void` - 开始交互
- `end(pluginId: string, type: string): void` - 结束交互

#### Runtime 类

运行时管理器，处理上下文执行和值处理。

```typescript
// Runtime 类的类型定义
class Runtime {
  // 运行时的方法和属性
}
```

**属性：**
- `currentContext: { type: any; value: any } | undefined` - 当前上下文

**方法：**
- `runInContext<T>(type: any, handler: () => T, contextValue?: any): T` - 在上下文中运行
- `getValue<T>(engine: Engine<any>, options: { paths: any[]; matcher?: (engine: Engine<any>) => BehaviorSubject<any>; selector?: (value: T, context?: any) => any }): any` - 获取值
- `static getInstance(): Runtime` - 获取单例实例
- `static DEFAULT_VALUE_HANDLER: <T>(value: BehaviorSubject<T> | T, options: { paths: string[]; selector?: (value: T, context?: any) => any; context?: any }) => T` - 默认值处理器

#### DataManager 类

数据管理器，管理数据的增删改查和版本控制。

```typescript
// DataManager 类的类型定义
class DataManager<TData extends IData = IData, TTag extends string = any> {
  // 数据管理器的方法和属性
}
```

**属性：**
- `readonly tag: TTag` - 标签
- `readonly dataMap$: BehaviorSubject<Map<string, TData>>` - 数据映射
- `readonly patchVersion$: BehaviorSubject<number>` - 补丁版本
- `readonly mapVersion$: BehaviorSubject<number>` - 映射版本
- `version: number` - 版本号

**方法：**
- `init(initialDataList: TData[]): void` - 初始化
- `getDataList$(): Observable<TData[]>` - 获取数据列表流
- `getDataList(): TData[]` - 获取数据列表
- `getData(id: string): TData | undefined` - 获取数据
- `getOperations$(): Observable<DataOperation<TData>>` - 获取操作流
- `dispatch(operation: DataOperation<TData>): void` - 派发操作
- `getDraftDataList(draftId: string): TData[] | undefined` - 获取草稿数据列表

#### DualRelation 类

双向关系管理器，管理父子节点关系。

```typescript
// DualRelation 类的类型定义
class DualRelation<P, C> {
  // 双向关系管理器的方法和属性
}
```

**属性：**
- `version: BehaviorSubject<number>` - 版本号

**方法：**
- `addRelation(parent: P, child: C): void` - 添加关系
- `removeRelation(parent: P, child: C): void` - 删除关系
- `getChildren(parent: P): C[]` - 获取子节点
- `getParent(child: C): P | undefined` - 获取父节点
- `clear(): void` - 清空关系

#### Element 接口和 DOMElement 类

元素抽象，提供跨平台的元素操作接口。

```typescript
interface Element {
  classList: ElementClassList
  attribute: ElementAttribute
  uniqSelector: string | undefined
  getRect: () => ElementRect
  addEventListener: EventListenerDefinition
  removeEventListener: EventListenerDefinition
  query: (selector: string) => Element | null
  queryAll: (selector: string) => Element[]
}

class DOMElement implements Element {
  constructor(readonly dom: HTMLElement) {}
  static fromDOM(dom: HTMLElement): DOMElement
}
```

### 🎭 类型定义

#### 基础类型
```typescript
type HorizontalAlignment = 'left' | 'right'
type VerticalAlignment = 'top' | 'bottom'
type Direction = 'horizontal' | 'vertical'
type IRecord = Record<string, any>
type Position = VerticalAlignment | HorizontalAlignment | `${HorizontalAlignment}-${VerticalAlignment}` | 'center'
type RenderType = (...args: any[]) => any
```

#### 节点相关类型
```typescript
interface NodePosition {
  x: number
  y: number
}

interface NodeMeasured {
  width: number
  height: number
}

interface Node<TData extends IRecord = IRecord> {
  id: string
  type?: string
  position: NodePosition
  measured?: NodeMeasured
  data: TData
}

interface NodeProps<TData extends IRecord = IRecord> {
  node: Node<TData>
  renderVersion?: number
}

type NodeRenderType<TD extends IRecord = any, TR = any> = (props: NodeProps<TD>) => TR
type NodeOperation<T extends IRecord = any> = DataOperation<Node<T>>
type NodeOperationPipe<T extends IRecord = any> = DataOperationPipe<Node<T>>
type NodeOperatorFunction<T extends IRecord = any, TArgs extends any[] = any[]> = (...args: TArgs) => NodeOperation<T>[]
```

#### 边相关类型
```typescript
interface EdgeConfig {
  sourcePosition?: Position
  targetPosition?: Position
  sourceYOffset?: string | number
  sourceXOffset?: string | number
  targetYOffset?: string | number
  targetXOffset?: string | number
}

interface Edge<TData extends IRecord = IRecord> extends EdgeConfig {
  id: string
  source: string
  target: string
  type?: string
  data?: TData
}

interface EdgeProps<TData extends IRecord = IRecord> {
  edge: Edge<TData>
  sourceX: number
  sourceY: number
  targetX: number
  targetY: number
  renderVersion?: number
}

type EdgeRenderType<TD extends IRecord = any, TR = any> = (props: EdgeProps<TD>) => TR

interface EdgeRender<TD extends IRecord = any, TR = any> {
  renderer: EdgeRenderType<TD, TR>
  config: Required<EdgeConfig>
}

type EdgeOperation<T extends IRecord = any> = DataOperation<Edge<T>>
type EdgeOperationPipe<T extends IRecord = any> = DataOperationPipe<Edge<T>>
type EdgeOperatorFunction<T extends IRecord = any, TArgs extends any[] = any[]> = (...args: TArgs) => EdgeOperation<T>[]
```

#### 容器类型
```typescript
interface Container {
  width: number
  height: number
  direction: Direction
}
```

#### 层级类型
```typescript
enum Layer {
  Canvas = 0,
  Background = 4,
  Edges = 16,
  Nodes = 64,
  Foreground = 256,
}

interface LayerComponent<TRenderType> {
  plugin: string
  name: string
  layer: Layer
  render: TRenderType
  offset?: number
}
```

#### 插件类型
```typescript
interface IPlugin<TPluginName extends string = string, TPluginConfig extends IRecord | undefined = any> {
  name: TPluginName
  pluginId: string
  description?: string
  onInit?: (config: TPluginConfig) => void
  onConfigChange?: (config: TPluginConfig) => void
  onDestroy?: () => void
}

type Plugin<TName extends string = string, TConfig extends IRecord | undefined = any, TRenderType extends RenderType = RenderType> = new (__: TConfig) => BasePlugin<TName, TConfig, TRenderType>

interface PluginData {} // 模块扩展接口
interface PluginTools {} // 模块扩展接口
interface EngineTools {} // 模块扩展接口

interface IPluginInfo {
  name: string
  description?: string
}

interface IToolInfo {
  type: 'function'
  name: string
  description: string
  parameters: Schema
}
```

#### 数据操作类型
```typescript
interface IData {
  id: string
}

interface DataAddOperation<T extends IData = IData> {
  type: 'add'
  data: T
}

interface DataRemoveOperation<_T extends IData = IData> {
  type: 'remove'
  id: string
}

interface DataUpdateOperation<T extends IData = IData> {
  type: 'update'
  id: string
  data: T
}

interface DataBatchOperation<T extends IData = IData> {
  type: 'batch'
  operations: DataOperation<T>[]
  isInit?: boolean
}

interface DataStartDraftOperation<T extends IData = IData> {
  type: 'startDraft'
  draftId: string
  data: T
}

interface DataCommitDraftOperation<T extends IData = IData> {
  type: 'commitDraft'
  draftId: string
  data: T
}

interface DataDiscardDraftOperation<T extends IData = IData> {
  type: 'discardDraft'
  draftId: string
}

interface DataDraftOperation<T extends IData = IData> {
  type: 'draft'
  draftId: string
  operation: DataOperation<T>
}

type DataOperation<T extends IData = IData> = DataAddOperation<T> | DataRemoveOperation<T> | DataUpdateOperation<T> | DataBatchOperation<T> | DataStartDraftOperation<T> | DataCommitDraftOperation<T> | DataDiscardDraftOperation<T> | DataDraftOperation<T>

interface DataOperationPipe<T extends IData = IData> {
  transform?: (operations$: Subject<DataOperation<T>>) => OperatorFunction<DataOperation<T>, DataOperation<T>>
  preOperation?: (operations$: Subject<DataOperation<T>>) => OperatorFunction<DataOperation<T>, DataOperation<T>>
  postOperation?: (operations$: Subject<DataOperation<T>>) => OperatorFunction<{ dataMap: Map<string, T>, operations: DataOperation<T>[] }, { dataMap: Map<string, T>, operations: DataOperation<T>[] }>
}
```

#### 交互类型
```typescript
enum InteractionPriority {
  InputActive = 2000,
  EntityConnectDrag = 1930,
  EntityMeasureDrag = 1920,
  EntityPositionDrag = 1910,
  ContinuousDrag = 1900,
  ContextMenu = 1800,
  KeyboardShortcut = 1700,
  ClickSelection = 1600,
  DoubleClickEdit = 1500,
  MarqueeSelection = 1300,
  LassoSelection = 1250,
  LongPress = 1100,
  HoverTooltip = 1000,
  CanvasPan = 900,
  CanvasZoom = 800,
  CanvasContextMenu = 700,
  CanvasClick = 600,
  MultiTouchGesture = 500,
}

interface Interaction {
  pluginId: string
  type: string
  priority: InteractionPriority
  active?: boolean
}
```

#### 元素类型
```typescript
interface ElementClassList {
  toString: () => string
  add: (...tokens: string[]) => void
  contains: (token: string) => boolean
  remove: (...tokens: string[]) => void
}

interface ElementAttribute {
  get: (name: string) => string | null
  set: (name: string, value: string) => void
  remove: (name: string) => void
}

interface ElementRect {
  height: number
  width: number
  x: number
  y: number
}

interface EventListenerDefinition<E = any> {
  (type: string, listener: ((evt: E) => void) | EventListenerObject<E>, options?: boolean | AddEventListenerOptions): void
}
```

#### 引擎类型
```typescript
interface EngineOptions<TNode extends IRecord = IRecord, TEdge extends IRecord = IRecord> {
  container: Container
  plugins?: Plugin[]
  pluginConfig?: IRecord
  nodes?: Node<TNode>[]
  edges?: Edge<TEdge>[]
  runtime?: IEngineRuntime
}

interface IEngineRuntime {
  render?: {
    getValue: <T, R = T>(value: BehaviorSubject<T> | T, options: {
      paths: string[]
      selector?: (value: T, context?: any) => R
      context?: any
    }) => T | R
  }
}

interface CallToolMethod {
  <T extends keyof PluginTools, TP extends keyof PluginTools[T]>(pluginName: Extract<T, keyof PluginTools>, toolName: Extract<TP, keyof PluginTools[T]>, ...args: Parameters<PluginTools[T][TP]>): ReturnType<PluginTools[T][TP]>
  <T extends keyof EngineTools>(engineToolName: Extract<T, keyof EngineTools>, ...args: Parameters<EngineTools[T]>): ReturnType<EngineTools[T]>
}
```

### 🛠️ 工具函数

#### 钩子函数
```typescript
function use<T>(hook: () => T): T
function use<T, TContext>(hook: () => { __contextRef__: T, __contextValue__: TContext }, context: TContext): T
```

#### 层级函数
```typescript
function getLayerRenders<TRenderType extends RenderType = RenderType>(plugin: IPlugin): LayerComponent<TRenderType>[]
```

#### 工具函数（来自 @knotx/utils）
```typescript
// BEM 样式类名工具
function bem(block: string, element?: string, modifier?: string, prefix?: string): string
function addBemModifier(className: string, modifier: string): string

// 唯一 ID 生成
function generateId(): string

// 符号工具
function getSymbol<TName extends string>(name: TName): symbol & { __knotx__?: TName }

// 数据操作工具
function isInitOperation<T extends IData>(operation: DataOperation<T>): operation is DataInitBatchOperation<T>
function isDraftOperation<T extends IData>(operation: DataOperation<T>): operation is Extract<DataOperation<T>, { draftId: string }>
function isEmptyBatchOperation<T extends IData>(operation: DataOperation<T>): operation is DataEmptyBatchOperation<T>
function flattenOperations<T extends IData>(operations: DataOperation<T>[]): DataOperation<T>[]
function emptyOperation<T extends IData>(): DataEmptyBatchOperation<T>
function buildDiffOperation<T extends IData>(previousDataMap: Map<string, T>, dataMap: Map<string, T>, compare?: (a: T | undefined, b: T | undefined) => boolean): DataBatchOperation<T>
```

### 📘 使用示例

#### 基本使用
```typescript
import { Engine } from '@knotx/core'

// 创建引擎
const engine = new Engine({
  container: { width: 800, height: 600, direction: 'horizontal' },
  nodes: [
    { id: '1', position: { x: 100, y: 100 }, data: { label: 'Node 1' } }
  ],
  edges: []
})

// 获取数据
const nodes = engine.getNodes()
const node = engine.getNode({ id: '1' })
```

#### 插件开发
```typescript
import { BasePlugin, Layer } from '@knotx/core'

class MyPlugin extends BasePlugin<'my-plugin', { theme: string }> {
  name = 'my-plugin' as const

  onInit(config: { theme: string }) {
    // 插件初始化逻辑
  }
}
```

#### 数据操作
```typescript
import { DataManager } from '@knotx/core'

const dataManager = new DataManager<Node>('nodes')
dataManager.init([
  { id: '1', position: { x: 0, y: 0 }, data: {} }
])

// 添加数据
dataManager.dispatch({
  type: 'add',
  data: { id: '2', position: { x: 100, y: 100 }, data: {} }
})
```

#### 交互管理
```typescript
import { InteractionManager, InteractionPriority } from '@knotx/core'

const interaction = new InteractionManager()
const cancel = interaction.start('plugin-id', 'drag', InteractionPriority.EntityPositionDrag)

// 检查是否可以交互
if (interaction.canInteract(InteractionPriority.ClickSelection)) {
  // 执行交互逻辑
}

// 取消交互
cancel()
```

## 🗂️ 文件目录结构

```
packages/core/
├── src/
│   ├── index.ts           # 主导出文件
│   ├── definition.ts      # 类型定义和接口
│   ├── engine.ts          # 核心引擎实现
│   ├── element.ts         # 元素抽象层
│   ├── plugin.ts          # 插件基类
│   ├── interaction.ts     # 交互管理器
│   ├── runtime.ts         # 运行时管理
│   ├── layer.tsx          # 层级渲染
│   ├── use.ts             # 钩子函数
│   ├── data.ts            # 数据管理（重新导出）
│   └── utils.ts           # 工具函数（重新导出）
├── dist/                  # 构建输出目录
├── package.json           # 包配置文件
├── tsconfig.json          # TypeScript 配置
├── build.config.ts        # 构建配置
└── README.md              # 项目文档
```

## 🔧 高级用法

### 自定义插件开发

```typescript
import type { LayerComponent } from '@knotx/core'

import { BasePlugin, Layer } from '@knotx/core'

class CustomPlugin extends BasePlugin<'custom', { theme: string }> {
  name = 'custom' as const

  // 声明插件数据
  declare pluginData: {
    selectedNodes: string[]
    highlightColor: string
  }

  // 声明插件工具
  declare pluginTools: {
    selectNode: (nodeId: string) => void
    clearSelection: () => void
  }

  onInit(config: { theme: string }) {
    // 注册插件数据
    this.engine.registerPluginData('custom', 'selectedNodes', new BehaviorSubject([]))
    this.engine.registerPluginData('custom', 'highlightColor', new BehaviorSubject('#blue'))

    // 注册插件工具
    this.engine.registerPluginTool('custom', 'selectNode', {
      description: '选择节点',
      parameters: { type: 'string' },
      func: (nodeId: string) => {
        // 实现选择逻辑
        console.log('Selecting node:', nodeId)
      }
    })

    // 注册层级组件
    this.engine.registerLayerComponent({
      plugin: this.name,
      name: 'selection-layer',
      layer: Layer.Foreground,
      render: this.renderSelection.bind(this)
    })
  }

  renderSelection() {
    return '<div class="selection-overlay">Selection Layer</div>'
  }
}
```

### 数据操作管道

```typescript
import type { EdgeOperation, NodeOperation } from '@knotx/core'

// 节点操作管道
engine.addNodePipe((operations: NodeOperation[]) => {
  return operations.map((op) => {
    if (op.type === 'update') {
      // 在更新前验证数据
      if (!validateNodeData(op.data)) {
        throw new Error('Invalid node data')
      }
    }
    return op
  })
})

// 边操作管道
engine.addEdgePipe((operations: EdgeOperation[]) => {
  return operations.filter((op) => {
    // 过滤无效的边操作
    return op.source !== op.target
  })
})
```

---

## 📄 开源协议

本项目采用 [MIT License](../../LICENSE) 开源协议。

---

## 🔗 相关链接

- [项目主页](https://github.com/boenfu/knotx)
- [问题反馈](https://github.com/boenfu/knotx/issues)
- [贡献指南](../../CONTRIBUTING.md)

---

Made with ❤️ by the Knotx team