@knotx/core
v0.5.7
Published
Core for Knotx
Downloads
449
Readme
@knotx/core
English | 中文
📦 安装
npm install @knotx/coreyarn add @knotx/corepnpm add @knotx/core📖 概述
@knotx/core 是 Knotx 图形编辑器的核心包,提供了构建可视化图形编辑器所需的基础架构。它包含引擎管理、插件系统、交互管理、元素操作、层级渲染等核心功能,为开发者提供了强大而灵活的图形编辑能力。
🚀 快速开始
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 的核心,负责管理整个图形编辑器的状态和行为。
// 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 类
插件基类,提供插件开发的基础功能。
// BasePlugin 抽象类的类型定义
abstract class BasePlugin<TPluginName extends string = string, TPluginConfig extends IRecord | undefined = undefined, TRenderType extends RenderType = RenderType> {
// 插件的抽象方法和属性
}属性:
abstract name: TPluginName- 插件名称pluginId: string- 插件唯一IDprotected subscriptions: (SubscriptionLike | (() => void))[]- 订阅集合protected callTool: Engine['callTool']- 调用工具方法
方法:
onInit?(config: TPluginConfig): void- 初始化回调onConfigChange?(config: TPluginConfig): void- 配置变更回调onDestroy?(): void- 销毁回调
InteractionManager 类
交互管理器,负责管理用户交互的优先级和状态。
// 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 类
运行时管理器,处理上下文执行和值处理。
// 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 类
数据管理器,管理数据的增删改查和版本控制。
// 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 类
双向关系管理器,管理父子节点关系。
// 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 类
元素抽象,提供跨平台的元素操作接口。
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
}🎭 类型定义
基础类型
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节点相关类型
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>[]边相关类型
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>[]容器类型
interface Container {
width: number
height: number
direction: Direction
}层级类型
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
}插件类型
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
}数据操作类型
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>[] }>
}交互类型
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
}元素类型
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
}引擎类型
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]>
}🛠️ 工具函数
钩子函数
function use<T>(hook: () => T): T
function use<T, TContext>(hook: () => { __contextRef__: T, __contextValue__: TContext }, context: TContext): T层级函数
function getLayerRenders<TRenderType extends RenderType = RenderType>(plugin: IPlugin): LayerComponent<TRenderType>[]工具函数(来自 @knotx/utils)
// 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>📘 使用示例
基本使用
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' })插件开发
import { BasePlugin, Layer } from '@knotx/core'
class MyPlugin extends BasePlugin<'my-plugin', { theme: string }> {
name = 'my-plugin' as const
onInit(config: { theme: string }) {
// 插件初始化逻辑
}
}数据操作
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: {} }
})交互管理
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 # 项目文档🔧 高级用法
自定义插件开发
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>'
}
}数据操作管道
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 开源协议。
🔗 相关链接
Made with ❤️ by the Knotx team
