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

# @knotx/plugins-selection

选择插件，为 KnotX 提供元素选择功能。

## 安装

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

## 概述

Selection 插件为 KnotX 提供了强大的元素选择功能，支持鼠标框选、键盘操作和程序化选择。该插件基于 `@knotx/viselect` 库实现，提供了灵活的选择规则和丰富的选择事件。

## 实现原理

Selection 插件的核心实现原理：

1. **选择区域管理**：使用 `@knotx/viselect` 库创建选择区域，支持拖拽选择
2. **规则系统**：通过规则系统定义不同类型元素的选择行为
3. **事件处理**：提供完整的选择事件生命周期管理
4. **状态同步**：与画布变换状态同步，确保选择区域的准确性

## 依赖关系

### 核心依赖
- `@knotx/core`：提供基础插件架构
- `@knotx/decorators`：提供装饰器支持
- `@knotx/viselect`：提供选择区域功能

### 插件依赖
- `@knotx/plugins-canvas`：获取画布变换状态和容器信息（可选）

## API 文档

### 主要类

#### Selection

选择插件的主要类，继承自 `BasePlugin`。

```typescript
export class Selection extends BasePlugin<'selection', SelectionConfig> {
  name = 'selection' as const
}
```

### 配置选项

#### SelectionConfig

```typescript
export interface SelectionConfig {
  /** 选择区域的类名 */
  selectionAreaClassName?: string
  /** 是否启用拖拽选择 */
  enableDrag?: boolean
  /** 选择规则 */
  rules?: SelectionRule[]
  /** 是否禁用默认规则 */
  disableDefaultRules?: boolean
  /** 选择容器 */
  container?: string
}
```

#### SelectionRule

```typescript
export interface SelectionRule {
  /** 资源类型 */
  type: string
  /** 元素的类名 */
  className: string
  /** ID 获取函数 */
  idGetter: (element: Element) => string
  /** 激活状态的类名 */
  activeClassName?: string
  /** 选择回调函数 */
  onSelected?: (context: SelectionContext) => boolean
  /** 数据选择器类型 */
  dataSelectorType?: string
  /** 是否启用规则 */
  enabled?: boolean
}
```

### 插件数据 (PluginData)

Selection 插件提供以下数据：

```typescript
interface PluginData {
  selection: {
    selected: SelectionSelectedItem[]
    registerRules: (rules: SelectionRule[]) => () => void
    setRuleEnabled: (type: string, enabled: boolean) => void
    setClearSelectionBeforeStart: (isClearSelectionBeforeStart: boolean) => void
    setElementOverlap: (overlap: OverlapMode) => void
    resolveSelectables: () => void
  }
}
```

### 插件工具 (PluginTools)

Selection 插件提供以下工具方法：

```typescript
interface PluginTools {
  selection: {
    select: (items: SelectionSelectedItem[]) => void
    deselect: (items: SelectionSelectedItem[]) => void
    getSelected: () => SelectionSelectedItem[]
    clearSelection: () => void
  }
}
```

### 类型定义

#### SelectionSelectedItem

```typescript
export interface SelectionSelectedItem {
  type: string
  id: string
}
```

## 使用示例

### 基本用法

```typescript
import { Selection } from '@knotx/plugins-selection'

const engine = new Engine({
  plugins: [Selection],
  pluginConfig: {
    selection: {
      enableDrag: true,
    },
  },
})
```

### 自定义选择规则

```typescript
const engine = new Engine({
  plugins: [Selection],
  pluginConfig: {
    selection: {
      rules: [
        {
          type: 'custom-node',
          className: 'custom-node-wrapper',
          idGetter: element => element.getAttribute('data-node-id') || '',
          activeClassName: 'custom-node-selected',
          onSelected: (context) => {
            console.log('Custom node selected:', context.id)
            return true // 返回 true 表示允许选择
          },
        },
      ],
    },
  },
})
```

### 程序化选择

```typescript
// 选择特定元素
engine.callTool('selection', 'select', [
  { type: 'node', id: 'node-1' },
  { type: 'node', id: 'node-2' },
])

// 取消选择特定元素
engine.callTool('selection', 'deselect', [
  { type: 'node', id: 'node-1' },
])

// 获取当前选择的元素
const selected = engine.callTool('selection', 'getSelected')
console.log('Selected items:', selected)

// 清空所有选择
engine.callTool('selection', 'clearSelection')
```

### 动态管理选择规则

```typescript
// 获取插件数据
const selectionData = engine.getPluginData('selection')

// 注册新规则
const unregister = selectionData.registerRules([
  {
    type: 'edge',
    className: 'edge-wrapper',
    idGetter: element => element.getAttribute('data-edge-id') || '',
    activeClassName: 'edge-selected',
  },
])

// 启用/禁用规则
selectionData.setRuleEnabled('edge', false)

// 设置重叠模式
selectionData.setElementOverlap('invert') // 'keep' | 'drop' | 'invert'

// 取消注册规则
unregister()
```

### 监听选择变化

```typescript
class MyPlugin extends BasePlugin {
  @inject.selection.selected()
  selected!: SelectionSelectedItem[]

  @subscribe.selection.selected()
  onSelectedChange(selected: SelectionSelectedItem[]) {
    console.log('Selection changed:', selected)
  }
}
```

### 与 Canvas 插件集成

```typescript
import { Canvas } from '@knotx/plugins-canvas'
import { Selection } from '@knotx/plugins-selection'

const engine = new Engine({
  plugins: [Canvas, Selection],
  pluginConfig: {
    canvas: {
      minScale: 0.1,
      maxScale: 3,
    },
    selection: {
      enableDrag: true,
      container: '.canvas-container', // 指定选择容器
    },
  },
})
```

## 高级功能

### 自定义选择行为

```typescript
const engine = new Engine({
  plugins: [Selection],
  pluginConfig: {
    selection: {
      rules: [
        {
          type: 'node',
          className: 'node-wrapper',
          idGetter: element => element.getAttribute('data-node-id') || '',
          onSelected: (context) => {
            const { element, type, id, isDragging, getSelectedElements, select, deselect } = context

            // 自定义选择逻辑
            if (isDragging) {
              // 拖拽选择时的逻辑
              return true
            }

            // 单击选择时的逻辑
            const selectedElements = getSelectedElements()
            if (selectedElements.node && selectedElements.node.length > 0) {
              // 已有选择，执行多选逻辑
              return true
            }

            return true
          },
        },
      ],
    },
  },
})
```

### 选择区域样式自定义

```typescript
const engine = new Engine({
  plugins: [Selection],
  pluginConfig: {
    selection: {
      selectionAreaClassName: 'custom-selection-area',
    },
  },
})
```

```css
.custom-selection-area {
  background: rgba(74, 144, 226, 0.1);
  border: 2px dashed #4A90E2;
  border-radius: 4px;
}
```

## 文件目录结构

```
packages/plugins-selection/
├── src/
│   ├── selection.tsx           # 主要实现文件
│   └── index.ts               # 导出文件
├── dist/                      # 构建输出目录
├── package.json              # 包配置文件
├── build.config.ts           # 构建配置
├── tsconfig.json             # TypeScript 配置
├── eslint.config.mjs         # ESLint 配置
└── CHANGELOG.md              # 更新日志
```

### 核心文件说明

- **selection.tsx**：包含 Selection 插件的主要实现，包括选择区域管理、规则系统和事件处理
- **index.ts**：导出 Selection 类和相关类型定义

## 注意事项

1. **性能考虑**：大量元素时，选择操作可能影响性能，建议使用虚拟化或分页
2. **事件冲突**：与其他交互插件（如拖拽）可能存在事件冲突，注意事件优先级
3. **内存管理**：及时清理不需要的选择规则，避免内存泄漏
4. **浏览器兼容性**：依赖现代浏览器的 DOM API，注意兼容性

## 许可证

MIT