npm package: @knotx/plugins-bounding
Description: Bounding Plugin for Knotx
Author: boenfu
Latest version: 0.5.10

# @knotx/plugins-bounding

边界框插件，为节点提供缩放和连接点功能。

## 安装

```bash
npm install @knotx/plugins-bounding
```

## 基本概述

`@knotx/plugins-bounding` 是 KnotX 框架的边界框插件，为节点提供缩放（resize）和连接点（connect handle）功能。支持多种缩放手柄位置、自定义样式、最小尺寸限制和惯性效果。

## 实现原理

该插件通过 InteractJS 实现交互功能：

1. **缩放手柄**：在节点周围提供可拖拽的缩放手柄
2. **连接点**：在节点边缘提供连接点，用于节点间连接
3. **边界框**：为选中节点显示边界框
4. **交互处理**：处理缩放和连接的拖拽交互

## 依赖插件

该插件依赖以下包：

- `@knotx/core` - 核心功能
- `@knotx/decorators` - 装饰器支持
- `@knotx/plugins-canvas` - 画布插件（peer dependency）
- `@knotx/jsx` - JSX 支持（peer dependency）
- `@knotx/render` - 渲染工具
- `interactjs` - 交互库
- `rxjs` - 响应式编程

## API 使用

### 基本使用

```typescript
import { Bounding } from '@knotx/plugins-bounding'

// 创建边界框插件实例
const bounding = new Bounding()

// 在引擎中注册插件
engine.use(bounding)
```

### 配置选项

```typescript
const bounding = new Bounding().init({
  features: {
    resize: true,
    connect: true,
    boundingBox: true
  },
  sizes: {
    resizeHandle: 6,
    connectHandle: 8,
    boundingBoxThreshold: 200
  },
  styles: {
    resizeHandle: {
      fill: '#3b82f6',
      stroke: '#ffffff',
      strokeWidth: 1
    },
    connectHandle: {
      fill: '#10b981',
      stroke: '#ffffff',
      strokeWidth: 1,
      shadowColor: '#000000',
      shadowWidth: 1
    },
    boundingBox: {
      stroke: '#3b82f6',
      strokeWidth: 1
    }
  },
  resize: {
    minWidth: 50,
    minHeight: 30,
    inertia: false,
    handles: ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'left', 'right', 'top', 'bottom'],
    nodesType: ['default', 'custom']
  }
})
```

### 自定义缩放手柄属性

```tsx
const { getCustomResizeHandleAttributes } = useKnotX().bounding

// 为自定义节点添加缩放手柄
function ResizableNode({ node }) {
  return (
    <div style={{ position: 'relative' }}>
      <div>{node.data.label}</div>

      {/* 右下角缩放手柄 */}
      <div
        {...getCustomResizeHandleAttributes(node.id, 'bottom-right')}
        style={{
          position: 'absolute',
          right: -3,
          bottom: -3,
          width: 6,
          height: 6,
          backgroundColor: '#3b82f6',
          border: '1px solid #ffffff',
          borderRadius: '50%',
          cursor: 'nw-resize'
        }}
      />
    </div>
  )
}
```

### 完整示例

```typescript
import { Engine } from '@knotx/core'
import { Bounding } from '@knotx/plugins-bounding'
import { Canvas } from '@knotx/plugins-canvas'

const engine = new Engine()

// 注册依赖插件
engine.use(new Canvas())

// 注册边界框插件
engine.use(new Bounding().init({
  features: {
    resize: true,
    connect: true,
    boundingBox: true
  },
  sizes: {
    resizeHandle: 8,
    connectHandle: 10,
    boundingBoxThreshold: 150
  },
  styles: {
    resizeHandle: {
      fill: '#ec4899',
      stroke: '#ffffff',
      strokeWidth: 2
    },
    connectHandle: {
      fill: '#06b6d4',
      stroke: '#ffffff',
      strokeWidth: 2,
      shadowColor: 'rgba(0, 0, 0, 0.2)',
      shadowWidth: 2
    },
    boundingBox: {
      stroke: '#ec4899',
      strokeWidth: 2
    }
  },
  resize: {
    minWidth: 80,
    minHeight: 50,
    inertia: true,
    handles: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
    nodesType: ['default']
  }
}))

// 创建可缩放节点
engine.dispatchNodeOperation({
  type: 'add',
  node: {
    id: 'resizable-node',
    type: 'default',
    position: { x: 100, y: 100 },
    measured: { width: 120, height: 80 },
    data: { label: '可缩放节点' }
  }
})
```

## 配置接口

### BoundingConfig

```typescript
interface BoundingConfig {
  // 功能开关
  features?: {
    resize?: boolean
    connect?: boolean
    boundingBox?: boolean
  }
  // 尺寸配置
  sizes?: {
    resizeHandle?: number
    connectHandle?: number
    boundingBoxThreshold?: number
  }
  // 样式配置
  styles?: {
    resizeHandle?: {
      fill?: string
      stroke?: string
      strokeWidth?: number
    }
    connectHandle?: {
      fill?: string
      stroke?: string
      strokeWidth?: number
      shadowColor?: string
      shadowWidth?: number
    }
    boundingBox?: {
      stroke?: string
      strokeWidth?: number
    }
  }
  // resize 配置
  resize?: {
    minWidth?: number
    minHeight?: number
    inertia?: boolean
    handles?: ResizeHandlePosition[]
    nodesType?: string[]
  }
}
```

### ResizeHandlePosition

```typescript
type ResizeHandlePosition =
  | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
  | 'left' | 'right' | 'top' | 'bottom'
  | 'center'
```

### 插件数据接口

```typescript
declare module '@knotx/core' {
  interface PluginData {
    bounding: {
      getCustomResizeHandleAttributes: (nodeId: string, position: ResizeHandlePosition) => Record<string, string>
    }
  }
}
```

## 默认配置

```typescript
const DEFAULT_RESIZE_HANDLES: ResizeHandlePosition[] = [
  'top-left',
  'top-right',
  'bottom-left',
  'bottom-right'
]

const DEFAULT_CONNECT_HANDLE_POSITIONS = [
  { top: true },
  { right: true },
  { bottom: true },
  { left: true }
]

const DEFAULT_CONFIG = {
  features: {
    resize: true,
    connect: true,
    boundingBox: true
  },
  sizes: {
    resizeHandle: 6,
    connectHandle: 8,
    boundingBoxThreshold: 200
  },
  styles: {
    resizeHandle: {
      fill: '#3b82f6',
      stroke: '#ffffff',
      strokeWidth: 1
    },
    connectHandle: {
      fill: '#10b981',
      stroke: '#ffffff',
      strokeWidth: 1,
      shadowColor: '#000000',
      shadowWidth: 1
    },
    boundingBox: {
      stroke: '#3b82f6',
      strokeWidth: 1
    }
  },
  resize: {
    minWidth: 50,
    minHeight: 30,
    inertia: false,
    handles: DEFAULT_RESIZE_HANDLES,
    nodesType: []
  }
}
```

## 交互功能

### 缩放功能

- **多位置手柄**：支持 8 个方向的缩放手柄
- **最小尺寸**：可配置节点的最小宽度和高度
- **惯性效果**：支持启用/禁用惯性效果
- **节点类型过滤**：可指定哪些节点类型可以缩放

### 连接点功能

- **边缘连接点**：在节点四个边缘提供连接点
- **自定义样式**：支持自定义连接点的样式和阴影效果
- **动态显示**：根据交互状态动态显示/隐藏连接点

### 边界框功能

- **选择指示**：为选中节点显示边界框
- **尺寸阈值**：当节点尺寸超过阈值时才显示边界框
- **自定义样式**：支持自定义边界框的样式

## 事件处理

### 缩放事件

```typescript
interface ResizeEvent {
  nodeId: string
  startWidth: number
  startHeight: number
  startX: number
  startY: number
  position: string
}
```

### 缩放数据

```typescript
interface ResizeData {
  nodeId: string
  width: number
  height: number
}
```

## 文件目录结构

```
packages/plugins-bounding/
├── src/
│   ├── bounding.tsx         # 边界框插件主文件
│   └── index.ts             # 导出文件
├── dist/                    # 编译输出目录
├── CHANGELOG.md            # 变更日志
├── package.json            # 包配置
├── build.config.ts         # 构建配置
├── eslint.config.mjs       # ESLint 配置
└── tsconfig.json           # TypeScript 配置
```

## 许可证

MIT