@knotx/decorators 0.4.12 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.en.md +564 -0
  2. package/README.md +564 -0
  3. package/package.json +7 -7
package/README.en.md ADDED
@@ -0,0 +1,564 @@
1
+ # @knotx/decorators
2
+
3
+ A TypeScript decorator package for Knotx graph editor that simplifies plugin development.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @knotx/decorators
9
+ ```
10
+
11
+ Or with yarn:
12
+
13
+ ```bash
14
+ yarn add @knotx/decorators
15
+ ```
16
+
17
+ Or with pnpm:
18
+
19
+ ```bash
20
+ pnpm add @knotx/decorators
21
+ ```
22
+
23
+ ## Overview
24
+
25
+ `@knotx/decorators` provides a comprehensive set of TypeScript decorators to help developers create Knotx plugins more efficiently. These decorators encapsulate common plugin development patterns including node rendering, edge rendering, data management, lifecycle management, and more.
26
+
27
+ ## Key Features
28
+
29
+ - **Node Management**: Provides node type definition, operations, and pipeline processing
30
+ - **Edge Management**: Provides edge type definition, operations, and pipeline processing
31
+ - **Plugin Management**: Provides configuration management, lifecycle control, and tool function registration
32
+ - **Data Management**: Provides reactive data binding and dependency injection
33
+ - **UI Management**: Provides layer management and panel rendering
34
+
35
+ ## API Documentation
36
+
37
+ ### Node Decorators
38
+
39
+ #### @nodeType
40
+
41
+ Used to declare a method as a node renderer.
42
+
43
+ ```tsx
44
+ import { nodeType } from '@knotx/decorators'
45
+
46
+ class MyPlugin extends BasePlugin {
47
+ @nodeType('my-node')
48
+ renderNode(node: Node) {
49
+ return (
50
+ <div className="my-node">
51
+ <h3>{node.data.title}</h3>
52
+ <p>{node.data.content}</p>
53
+ </div>
54
+ )
55
+ }
56
+ }
57
+ ```
58
+
59
+ **Parameters**:
60
+ - `type: string` - Node type identifier
61
+
62
+ **Returns**: A React component or JSX element
63
+
64
+ #### @nodeOperator
65
+
66
+ Used to declare a method as a node operation function.
67
+
68
+ ```typescript
69
+ import { nodeOperator } from '@knotx/decorators'
70
+
71
+ class MyPlugin extends BasePlugin {
72
+ @nodeOperator()
73
+ addNode(x: number, y: number) {
74
+ return [{
75
+ type: 'add',
76
+ node: {
77
+ id: generateId(),
78
+ type: 'default',
79
+ position: { x, y },
80
+ data: { title: 'New Node' }
81
+ }
82
+ }]
83
+ }
84
+
85
+ @nodeOperator()
86
+ deleteNode(nodeId: string) {
87
+ return [{
88
+ type: 'remove',
89
+ nodeId
90
+ }]
91
+ }
92
+ }
93
+ ```
94
+
95
+ **Usage Notes**:
96
+ - Method must return an array of operations
97
+ - Operations are automatically dispatched to the engine for processing
98
+ - Supports batch operations
99
+
100
+ #### @nodePipe
101
+
102
+ Used to declare a method as a node operation pipeline for processing before and after node operations.
103
+
104
+ ```typescript
105
+ import { nodePipe } from '@knotx/decorators'
106
+ import { filter, map } from 'rxjs/operators'
107
+
108
+ class MyPlugin extends BasePlugin {
109
+ @nodePipe()
110
+ validateNodeOperation() {
111
+ return {
112
+ preOperation: () => filter((operation) => {
113
+ if (operation.type === 'add') {
114
+ return this.validateNode(operation.node)
115
+ }
116
+ return true
117
+ }),
118
+ postOperation: () => map((operation) => {
119
+ console.log('Operation completed:', operation)
120
+ return operation
121
+ })
122
+ }
123
+ }
124
+ }
125
+ ```
126
+
127
+ **Returns**: An object containing `preOperation` and `postOperation`
128
+
129
+ ### Edge Decorators
130
+
131
+ #### @edgeType
132
+
133
+ Used to declare a method as an edge renderer.
134
+
135
+ ```tsx
136
+ import { edgeType } from '@knotx/decorators'
137
+
138
+ class MyPlugin extends BasePlugin {
139
+ @edgeType('my-edge', {
140
+ sourcePosition: 'right',
141
+ targetPosition: 'left',
142
+ sourceXOffset: 10,
143
+ targetXOffset: -10
144
+ })
145
+ renderEdge(edge: Edge) {
146
+ return (
147
+ <g>
148
+ <path
149
+ d={edge.path}
150
+ stroke="#333"
151
+ strokeWidth={2}
152
+ fill="none"
153
+ />
154
+ <text x={edge.labelX} y={edge.labelY}>
155
+ {edge.label}
156
+ </text>
157
+ </g>
158
+ )
159
+ }
160
+ }
161
+ ```
162
+
163
+ **Parameters**:
164
+ - `type: string` - Edge type identifier
165
+ - `edgeConfig?: EdgeConfig` - Edge configuration options
166
+ - `sourcePosition: Position` - Source connection point position
167
+ - `targetPosition: Position` - Target connection point position
168
+ - `sourceXOffset: number` - Source connection point X offset
169
+ - `sourceYOffset: number` - Source connection point Y offset
170
+ - `targetXOffset: number` - Target connection point X offset
171
+ - `targetYOffset: number` - Target connection point Y offset
172
+
173
+ #### @edgeOperator
174
+
175
+ Used to declare a method as an edge operation function.
176
+
177
+ ```typescript
178
+ import { edgeOperator } from '@knotx/decorators'
179
+
180
+ class MyPlugin extends BasePlugin {
181
+ @edgeOperator()
182
+ addEdge(sourceId: string, targetId: string) {
183
+ return [{
184
+ type: 'add',
185
+ edge: {
186
+ id: generateId(),
187
+ source: sourceId,
188
+ target: targetId,
189
+ type: 'default'
190
+ }
191
+ }]
192
+ }
193
+ }
194
+ ```
195
+
196
+ #### @edgePipe
197
+
198
+ Used to declare a method as an edge operation pipeline.
199
+
200
+ ```typescript
201
+ import { edgePipe } from '@knotx/decorators'
202
+
203
+ class MyPlugin extends BasePlugin {
204
+ @edgePipe()
205
+ validateEdgeOperation() {
206
+ return {
207
+ preOperation: () => filter((operation) => {
208
+ if (operation.type === 'add') {
209
+ return this.validateEdge(operation.edge)
210
+ }
211
+ return true
212
+ })
213
+ }
214
+ }
215
+ }
216
+ ```
217
+
218
+ ### Plugin Decorators
219
+
220
+ #### @config
221
+
222
+ Automatically binds configuration management and lifecycle functions.
223
+
224
+ ```typescript
225
+ import { config } from '@knotx/decorators'
226
+
227
+ class MyPlugin extends BasePlugin {
228
+ @config()
229
+ config = {
230
+ theme: 'light',
231
+ showGrid: true,
232
+ gridSize: 20
233
+ }
234
+
235
+ // Configuration will be automatically updated on onInit and onConfigChange
236
+ }
237
+ ```
238
+
239
+ #### @register
240
+
241
+ Used to register plugin properties to the engine, making them accessible to other plugins via inject.
242
+
243
+ ```typescript
244
+ import { register } from '@knotx/decorators'
245
+
246
+ class HistoryPlugin extends BasePlugin {
247
+ @register('canUndo')
248
+ canUndo = false
249
+
250
+ @register('canRedo')
251
+ canRedo = false
252
+
253
+ @register('history')
254
+ get history() {
255
+ return this.historyStack
256
+ }
257
+ }
258
+ ```
259
+
260
+ #### @tool
261
+
262
+ Used to decorate tool functions in plugins, making them callable by the engine and other plugins.
263
+
264
+ ```typescript
265
+ import { tool } from '@knotx/decorators'
266
+
267
+ class MathPlugin extends BasePlugin {
268
+ @tool('Calculate distance between two points', {
269
+ type: 'object',
270
+ properties: {
271
+ x1: { type: 'number', description: 'X coordinate of first point' },
272
+ y1: { type: 'number', description: 'Y coordinate of first point' },
273
+ x2: { type: 'number', description: 'X coordinate of second point' },
274
+ y2: { type: 'number', description: 'Y coordinate of second point' }
275
+ },
276
+ required: ['x1', 'y1', 'x2', 'y2']
277
+ })
278
+ calculateDistance(params: { x1: number, y1: number, x2: number, y2: number }) {
279
+ const dx = params.x2 - params.x1
280
+ const dy = params.y2 - params.y1
281
+ return Math.sqrt(dx * dx + dy * dy)
282
+ }
283
+ }
284
+ ```
285
+
286
+ **Parameters**:
287
+ - `description: string` - Tool function description
288
+ - `parameters: Schema` - JSON Schema definition of parameters
289
+
290
+ #### @observable
291
+
292
+ Creates reactive properties that support data binding and change monitoring.
293
+
294
+ ```typescript
295
+ import { observable } from '@knotx/decorators'
296
+
297
+ class MyPlugin extends BasePlugin {
298
+ @observable()
299
+ selectedNodes: string[] = []
300
+
301
+ @observable()
302
+ zoomLevel = 1
303
+
304
+ updateSelection(nodeIds: string[]) {
305
+ this.selectedNodes = nodeIds // Automatically triggers change notification
306
+ }
307
+ }
308
+ ```
309
+
310
+ #### Lifecycle Decorators
311
+
312
+ ##### @OnInit
313
+
314
+ Called when the plugin is initialized.
315
+
316
+ ```typescript
317
+ import { OnInit } from '@knotx/decorators'
318
+
319
+ class MyPlugin extends BasePlugin {
320
+ @OnInit
321
+ initialize(config: PluginConfig) {
322
+ console.log('Plugin initialized with config:', config)
323
+ this.setupEventListeners()
324
+ }
325
+ }
326
+ ```
327
+
328
+ ##### @OnDestroy
329
+
330
+ Called when the plugin is destroyed.
331
+
332
+ ```typescript
333
+ import { OnDestroy } from '@knotx/decorators'
334
+
335
+ class MyPlugin extends BasePlugin {
336
+ @OnDestroy
337
+ cleanup() {
338
+ console.log('Plugin destroyed')
339
+ this.clearEventListeners()
340
+ }
341
+ }
342
+ ```
343
+
344
+ ##### @OnConfigChange
345
+
346
+ Called when plugin configuration changes.
347
+
348
+ ```typescript
349
+ import { OnConfigChange } from '@knotx/decorators'
350
+
351
+ class MyPlugin extends BasePlugin {
352
+ @OnConfigChange
353
+ handleConfigChange(newConfig: PluginConfig) {
354
+ console.log('Config changed:', newConfig)
355
+ this.applyNewConfig(newConfig)
356
+ }
357
+ }
358
+ ```
359
+
360
+ ### Engine Decorators
361
+
362
+ #### @inject
363
+
364
+ Used to inject engine variables or data from other plugins.
365
+
366
+ ```typescript
367
+ import { inject } from '@knotx/decorators'
368
+
369
+ class MyPlugin extends BasePlugin {
370
+ // Inject engine variables
371
+ @inject.nodes()
372
+ nodes: Node[] = []
373
+
374
+ // Inject engine methods
375
+ @inject.getNodeById()
376
+ getNodeById!: (id: string) => Node | undefined
377
+
378
+ // Inject data from other plugins
379
+ @inject.history.canUndo()
380
+ canUndo = false
381
+
382
+ // Inject specific data using selectors
383
+ @inject.nodes((nodes: Node[]) => nodes.filter(n => n.selected))
384
+ selectedNodes: Node[] = []
385
+ }
386
+ ```
387
+
388
+ **Usage**:
389
+ - `@inject.property()` - Inject engine variables
390
+ - `@inject.property(selector)` - Inject engine variables with selector
391
+ - `@inject.pluginName.property()` - Inject plugin variables
392
+ - `@inject.methodName()` - Inject engine methods
393
+
394
+ #### @subscribe
395
+
396
+ Used to subscribe to changes in engine variables or data from other plugins.
397
+
398
+ ```typescript
399
+ import { subscribe } from '@knotx/decorators'
400
+
401
+ class MyPlugin extends BasePlugin {
402
+ // Subscribe to engine variables
403
+ @subscribe.nodes()
404
+ handleNodesChange(nodes: Node[]) {
405
+ console.log('Nodes changed:', nodes)
406
+ this.updateUI(nodes)
407
+ }
408
+
409
+ // Subscribe to data from other plugins
410
+ @subscribe.history.canUndo()
411
+ handleUndoStateChange(canUndo: boolean) {
412
+ this.updateUndoButton(canUndo)
413
+ }
414
+ }
415
+ ```
416
+
417
+ ### Layer Decorators
418
+
419
+ #### @layer
420
+
421
+ Used to declare the render layer of a method.
422
+
423
+ ```tsx
424
+ import { Layer } from '@knotx/core'
425
+ import { layer } from '@knotx/decorators'
426
+
427
+ class MyPlugin extends BasePlugin {
428
+ @layer(Layer.BACKGROUND)
429
+ renderBackground() {
430
+ return (
431
+ <rect width="100%" height="100%" fill="#f0f0f0" />
432
+ )
433
+ }
434
+
435
+ @layer(Layer.FOREGROUND, 100)
436
+ renderOverlay() {
437
+ return (
438
+ <div className="overlay">
439
+ Overlay content
440
+ </div>
441
+ )
442
+ }
443
+ }
444
+ ```
445
+
446
+ **Parameters**:
447
+ - `layer: Layer` - Layer enum value
448
+ - `offset?: number` - Layer offset value
449
+
450
+ #### @panel
451
+
452
+ Used to create panel components.
453
+
454
+ ```tsx
455
+ import { panel } from '@knotx/decorators'
456
+
457
+ class MyPlugin extends BasePlugin {
458
+ @panel('top', { x: 10, y: 10 })
459
+ renderToolbar() {
460
+ return (
461
+ <div className="toolbar">
462
+ <button>Save</button>
463
+ <button>Load</button>
464
+ <button>Export</button>
465
+ </div>
466
+ )
467
+ }
468
+
469
+ @panel('bottom-right')
470
+ renderStatus() {
471
+ return (
472
+ <div className="status">
473
+ Ready
474
+ </div>
475
+ )
476
+ }
477
+ }
478
+ ```
479
+
480
+ **Parameters**:
481
+ - `position: Position` - Panel position ('top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right')
482
+ - `offset?: PanelOffset` - Panel offset
483
+ - `x?: number` - X-axis offset
484
+ - `y?: number` - Y-axis offset
485
+
486
+ ## Directory Structure
487
+
488
+ ```
489
+ src/
490
+ ├── index.ts # Main export file
491
+ ├── internal.ts # Internal utility functions
492
+ ├── edge/ # Edge-related decorators
493
+ │ ├── index.ts # Edge module exports
494
+ │ ├── edgeType.ts # Edge type decorator
495
+ │ ├── edgeOperator.ts # Edge operator decorator
496
+ │ └── edgePipe.ts # Edge pipe decorator
497
+ ├── engine/ # Engine-related decorators
498
+ │ ├── index.ts # Engine module exports
499
+ │ ├── inject/ # Inject decorators
500
+ │ │ ├── index.ts # Inject module exports
501
+ │ │ ├── inject-data.ts # Data injection implementation
502
+ │ │ ├── inject-decorator.ts # Inject decorator implementation
503
+ │ │ ├── inject-method.ts # Method injection implementation
504
+ │ │ ├── inject-plugin.ts # Plugin injection implementation
505
+ │ │ ├── injectable.ts # Injectable interface
506
+ │ │ ├── injectable-proxy.ts # Injection proxy implementation
507
+ │ │ ├── proxy.ts # Proxy object
508
+ │ │ └── types.ts # Type definitions
509
+ │ └── subscribe/ # Subscribe decorators
510
+ │ ├── index.ts # Subscribe module exports
511
+ │ ├── proxy.ts # Subscribe proxy object
512
+ │ ├── subscribe-data.ts # Data subscription implementation
513
+ │ ├── subscribe-decorator.ts # Subscribe decorator implementation
514
+ │ ├── subscribe-plugin.ts # Plugin subscription implementation
515
+ │ └── types.ts # Type definitions
516
+ ├── layer/ # Layer-related decorators
517
+ │ ├── index.ts # Layer module exports
518
+ │ ├── layer.ts # Layer decorator
519
+ │ └── panel.tsx # Panel decorator
520
+ ├── node/ # Node-related decorators
521
+ │ ├── index.ts # Node module exports
522
+ │ ├── nodeType.ts # Node type decorator
523
+ │ ├── nodeOperator.ts # Node operator decorator
524
+ │ └── nodePipe.ts # Node pipe decorator
525
+ └── plugin/ # Plugin-related decorators
526
+ ├── index.ts # Plugin module exports
527
+ ├── config.ts # Config decorator
528
+ ├── lifecycle.ts # Lifecycle decorators
529
+ ├── observable.ts # Observable decorator
530
+ ├── register.ts # Register decorator
531
+ └── tool.ts # Tool function decorator
532
+ ```
533
+
534
+ ## Dependencies
535
+
536
+ ### Peer Dependencies
537
+
538
+ - `@knotx/jsx` - Knotx JSX runtime support
539
+
540
+ ### Dependencies
541
+
542
+ - `@knotx/core` - Knotx core library
543
+ - `jsonschema` - JSON Schema validation library
544
+ - `lodash-es` - Utility functions library
545
+ - `rxjs` - Reactive programming library
546
+
547
+ ## License
548
+
549
+ MIT License
550
+
551
+ ## Contributing
552
+
553
+ Issues and Pull Requests are welcome. Before submitting code, please ensure:
554
+
555
+ 1. Code follows the project's ESLint rules
556
+ 2. Passes TypeScript type checking
557
+ 3. Includes necessary test cases
558
+ 4. Updates relevant documentation
559
+
560
+ ## More Information
561
+
562
+ - [Knotx Homepage](https://github.com/boenfu/knotx)
563
+ - [API Documentation](https://github.com/boenfu/knotx#readme)
564
+ - [Example Projects](https://github.com/boenfu/knotx/tree/main/apps/playground)
package/README.md ADDED
@@ -0,0 +1,564 @@
1
+ # @knotx/decorators
2
+
3
+ 一个为 Knotx 图形编辑器提供装饰器支持的 TypeScript 包,用于简化插件开发过程。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install @knotx/decorators
9
+ ```
10
+
11
+ 或者使用 yarn:
12
+
13
+ ```bash
14
+ yarn add @knotx/decorators
15
+ ```
16
+
17
+ 或者使用 pnpm:
18
+
19
+ ```bash
20
+ pnpm add @knotx/decorators
21
+ ```
22
+
23
+ ## 基本概述
24
+
25
+ `@knotx/decorators` 提供了一套完整的 TypeScript 装饰器,帮助开发者更高效地创建 Knotx 插件。这些装饰器封装了常见的插件开发模式,包括节点渲染、边渲染、数据管理、生命周期管理等功能。
26
+
27
+ ## 主要功能
28
+
29
+ - **节点管理**:提供节点类型定义、操作和管道处理
30
+ - **边管理**:提供边类型定义、操作和管道处理
31
+ - **插件管理**:提供配置管理、生命周期控制和工具函数注册
32
+ - **数据管理**:提供响应式数据绑定和依赖注入
33
+ - **UI 管理**:提供层级管理和面板渲染
34
+
35
+ ## API 文档
36
+
37
+ ### 节点相关装饰器
38
+
39
+ #### @nodeType
40
+
41
+ 用于声明一个方法作为节点渲染器。
42
+
43
+ ```tsx
44
+ import { nodeType } from '@knotx/decorators'
45
+
46
+ class MyPlugin extends BasePlugin {
47
+ @nodeType('my-node')
48
+ renderNode(node: Node) {
49
+ return (
50
+ <div className="my-node">
51
+ <h3>{node.data.title}</h3>
52
+ <p>{node.data.content}</p>
53
+ </div>
54
+ )
55
+ }
56
+ }
57
+ ```
58
+
59
+ **参数**:
60
+ - `type: string` - 节点类型标识符
61
+
62
+ **返回值**:返回一个 React 组件或 JSX 元素
63
+
64
+ #### @nodeOperator
65
+
66
+ 用于声明一个方法作为节点操作函数。
67
+
68
+ ```typescript
69
+ import { nodeOperator } from '@knotx/decorators'
70
+
71
+ class MyPlugin extends BasePlugin {
72
+ @nodeOperator()
73
+ addNode(x: number, y: number) {
74
+ return [{
75
+ type: 'add',
76
+ node: {
77
+ id: generateId(),
78
+ type: 'default',
79
+ position: { x, y },
80
+ data: { title: 'New Node' }
81
+ }
82
+ }]
83
+ }
84
+
85
+ @nodeOperator()
86
+ deleteNode(nodeId: string) {
87
+ return [{
88
+ type: 'remove',
89
+ nodeId
90
+ }]
91
+ }
92
+ }
93
+ ```
94
+
95
+ **使用说明**:
96
+ - 方法必须返回一个操作数组
97
+ - 操作会自动分发到引擎进行处理
98
+ - 支持批量操作
99
+
100
+ #### @nodePipe
101
+
102
+ 用于声明一个方法作为节点操作管道,在节点操作执行前后进行处理。
103
+
104
+ ```typescript
105
+ import { nodePipe } from '@knotx/decorators'
106
+ import { filter, map } from 'rxjs/operators'
107
+
108
+ class MyPlugin extends BasePlugin {
109
+ @nodePipe()
110
+ validateNodeOperation() {
111
+ return {
112
+ preOperation: () => filter((operation) => {
113
+ if (operation.type === 'add') {
114
+ return this.validateNode(operation.node)
115
+ }
116
+ return true
117
+ }),
118
+ postOperation: () => map((operation) => {
119
+ console.log('Operation completed:', operation)
120
+ return operation
121
+ })
122
+ }
123
+ }
124
+ }
125
+ ```
126
+
127
+ **返回值**:返回一个包含 `preOperation` 和 `postOperation` 的对象
128
+
129
+ ### 边相关装饰器
130
+
131
+ #### @edgeType
132
+
133
+ 用于声明一个方法作为边渲染器。
134
+
135
+ ```tsx
136
+ import { edgeType } from '@knotx/decorators'
137
+
138
+ class MyPlugin extends BasePlugin {
139
+ @edgeType('my-edge', {
140
+ sourcePosition: 'right',
141
+ targetPosition: 'left',
142
+ sourceXOffset: 10,
143
+ targetXOffset: -10
144
+ })
145
+ renderEdge(edge: Edge) {
146
+ return (
147
+ <g>
148
+ <path
149
+ d={edge.path}
150
+ stroke="#333"
151
+ strokeWidth={2}
152
+ fill="none"
153
+ />
154
+ <text x={edge.labelX} y={edge.labelY}>
155
+ {edge.label}
156
+ </text>
157
+ </g>
158
+ )
159
+ }
160
+ }
161
+ ```
162
+
163
+ **参数**:
164
+ - `type: string` - 边类型标识符
165
+ - `edgeConfig?: EdgeConfig` - 边配置选项
166
+ - `sourcePosition: Position` - 源连接点位置
167
+ - `targetPosition: Position` - 目标连接点位置
168
+ - `sourceXOffset: number` - 源连接点 X 偏移
169
+ - `sourceYOffset: number` - 源连接点 Y 偏移
170
+ - `targetXOffset: number` - 目标连接点 X 偏移
171
+ - `targetYOffset: number` - 目标连接点 Y 偏移
172
+
173
+ #### @edgeOperator
174
+
175
+ 用于声明一个方法作为边操作函数。
176
+
177
+ ```typescript
178
+ import { edgeOperator } from '@knotx/decorators'
179
+
180
+ class MyPlugin extends BasePlugin {
181
+ @edgeOperator()
182
+ addEdge(sourceId: string, targetId: string) {
183
+ return [{
184
+ type: 'add',
185
+ edge: {
186
+ id: generateId(),
187
+ source: sourceId,
188
+ target: targetId,
189
+ type: 'default'
190
+ }
191
+ }]
192
+ }
193
+ }
194
+ ```
195
+
196
+ #### @edgePipe
197
+
198
+ 用于声明一个方法作为边操作管道。
199
+
200
+ ```typescript
201
+ import { edgePipe } from '@knotx/decorators'
202
+
203
+ class MyPlugin extends BasePlugin {
204
+ @edgePipe()
205
+ validateEdgeOperation() {
206
+ return {
207
+ preOperation: () => filter((operation) => {
208
+ if (operation.type === 'add') {
209
+ return this.validateEdge(operation.edge)
210
+ }
211
+ return true
212
+ })
213
+ }
214
+ }
215
+ }
216
+ ```
217
+
218
+ ### 插件相关装饰器
219
+
220
+ #### @config
221
+
222
+ 自动绑定配置管理和生命周期函数。
223
+
224
+ ```typescript
225
+ import { config } from '@knotx/decorators'
226
+
227
+ class MyPlugin extends BasePlugin {
228
+ @config()
229
+ config = {
230
+ theme: 'light',
231
+ showGrid: true,
232
+ gridSize: 20
233
+ }
234
+
235
+ // 配置会自动在 onInit 和 onConfigChange 时更新
236
+ }
237
+ ```
238
+
239
+ #### @register
240
+
241
+ 用于将插件的属性注册到引擎中,使其他插件可以通过 inject 访问。
242
+
243
+ ```typescript
244
+ import { register } from '@knotx/decorators'
245
+
246
+ class HistoryPlugin extends BasePlugin {
247
+ @register('canUndo')
248
+ canUndo = false
249
+
250
+ @register('canRedo')
251
+ canRedo = false
252
+
253
+ @register('history')
254
+ get history() {
255
+ return this.historyStack
256
+ }
257
+ }
258
+ ```
259
+
260
+ #### @tool
261
+
262
+ 用于装饰插件中的工具函数,使其能被引擎和其他插件调用。
263
+
264
+ ```typescript
265
+ import { tool } from '@knotx/decorators'
266
+
267
+ class MathPlugin extends BasePlugin {
268
+ @tool('计算两点距离', {
269
+ type: 'object',
270
+ properties: {
271
+ x1: { type: 'number', description: '第一个点的 X 坐标' },
272
+ y1: { type: 'number', description: '第一个点的 Y 坐标' },
273
+ x2: { type: 'number', description: '第二个点的 X 坐标' },
274
+ y2: { type: 'number', description: '第二个点的 Y 坐标' }
275
+ },
276
+ required: ['x1', 'y1', 'x2', 'y2']
277
+ })
278
+ calculateDistance(params: { x1: number, y1: number, x2: number, y2: number }) {
279
+ const dx = params.x2 - params.x1
280
+ const dy = params.y2 - params.y1
281
+ return Math.sqrt(dx * dx + dy * dy)
282
+ }
283
+ }
284
+ ```
285
+
286
+ **参数**:
287
+ - `description: string` - 工具函数描述
288
+ - `parameters: Schema` - 参数的 JSON Schema 定义
289
+
290
+ #### @observable
291
+
292
+ 创建响应式属性,支持数据绑定和变更监听。
293
+
294
+ ```typescript
295
+ import { observable } from '@knotx/decorators'
296
+
297
+ class MyPlugin extends BasePlugin {
298
+ @observable()
299
+ selectedNodes: string[] = []
300
+
301
+ @observable()
302
+ zoomLevel = 1
303
+
304
+ updateSelection(nodeIds: string[]) {
305
+ this.selectedNodes = nodeIds // 自动触发变更通知
306
+ }
307
+ }
308
+ ```
309
+
310
+ #### 生命周期装饰器
311
+
312
+ ##### @OnInit
313
+
314
+ 在插件初始化时调用。
315
+
316
+ ```typescript
317
+ import { OnInit } from '@knotx/decorators'
318
+
319
+ class MyPlugin extends BasePlugin {
320
+ @OnInit
321
+ initialize(config: PluginConfig) {
322
+ console.log('Plugin initialized with config:', config)
323
+ this.setupEventListeners()
324
+ }
325
+ }
326
+ ```
327
+
328
+ ##### @OnDestroy
329
+
330
+ 在插件销毁时调用。
331
+
332
+ ```typescript
333
+ import { OnDestroy } from '@knotx/decorators'
334
+
335
+ class MyPlugin extends BasePlugin {
336
+ @OnDestroy
337
+ cleanup() {
338
+ console.log('Plugin destroyed')
339
+ this.clearEventListeners()
340
+ }
341
+ }
342
+ ```
343
+
344
+ ##### @OnConfigChange
345
+
346
+ 在插件配置变更时调用。
347
+
348
+ ```typescript
349
+ import { OnConfigChange } from '@knotx/decorators'
350
+
351
+ class MyPlugin extends BasePlugin {
352
+ @OnConfigChange
353
+ handleConfigChange(newConfig: PluginConfig) {
354
+ console.log('Config changed:', newConfig)
355
+ this.applyNewConfig(newConfig)
356
+ }
357
+ }
358
+ ```
359
+
360
+ ### 引擎相关装饰器
361
+
362
+ #### @inject
363
+
364
+ 用于注入引擎变量或其他插件的数据。
365
+
366
+ ```typescript
367
+ import { inject } from '@knotx/decorators'
368
+
369
+ class MyPlugin extends BasePlugin {
370
+ // 注入引擎变量
371
+ @inject.nodes()
372
+ nodes: Node[] = []
373
+
374
+ // 注入引擎方法
375
+ @inject.getNodeById()
376
+ getNodeById!: (id: string) => Node | undefined
377
+
378
+ // 注入其他插件的数据
379
+ @inject.history.canUndo()
380
+ canUndo = false
381
+
382
+ // 使用选择器注入特定数据
383
+ @inject.nodes((nodes: Node[]) => nodes.filter(n => n.selected))
384
+ selectedNodes: Node[] = []
385
+ }
386
+ ```
387
+
388
+ **使用方式**:
389
+ - `@inject.property()` - 注入引擎变量
390
+ - `@inject.property(selector)` - 注入引擎变量并使用选择器
391
+ - `@inject.pluginName.property()` - 注入插件变量
392
+ - `@inject.methodName()` - 注入引擎方法
393
+
394
+ #### @subscribe
395
+
396
+ 用于订阅引擎变量或其他插件的数据变化。
397
+
398
+ ```typescript
399
+ import { subscribe } from '@knotx/decorators'
400
+
401
+ class MyPlugin extends BasePlugin {
402
+ // 订阅引擎变量
403
+ @subscribe.nodes()
404
+ handleNodesChange(nodes: Node[]) {
405
+ console.log('Nodes changed:', nodes)
406
+ this.updateUI(nodes)
407
+ }
408
+
409
+ // 订阅其他插件的数据
410
+ @subscribe.history.canUndo()
411
+ handleUndoStateChange(canUndo: boolean) {
412
+ this.updateUndoButton(canUndo)
413
+ }
414
+ }
415
+ ```
416
+
417
+ ### 层相关装饰器
418
+
419
+ #### @layer
420
+
421
+ 用于声明方法所属的渲染层级。
422
+
423
+ ```tsx
424
+ import { Layer } from '@knotx/core'
425
+ import { layer } from '@knotx/decorators'
426
+
427
+ class MyPlugin extends BasePlugin {
428
+ @layer(Layer.BACKGROUND)
429
+ renderBackground() {
430
+ return (
431
+ <rect width="100%" height="100%" fill="#f0f0f0" />
432
+ )
433
+ }
434
+
435
+ @layer(Layer.FOREGROUND, 100)
436
+ renderOverlay() {
437
+ return (
438
+ <div className="overlay">
439
+ Overlay content
440
+ </div>
441
+ )
442
+ }
443
+ }
444
+ ```
445
+
446
+ **参数**:
447
+ - `layer: Layer` - 层级枚举值
448
+ - `offset?: number` - 层级偏移值
449
+
450
+ #### @panel
451
+
452
+ 用于创建面板组件。
453
+
454
+ ```tsx
455
+ import { panel } from '@knotx/decorators'
456
+
457
+ class MyPlugin extends BasePlugin {
458
+ @panel('top', { x: 10, y: 10 })
459
+ renderToolbar() {
460
+ return (
461
+ <div className="toolbar">
462
+ <button>Save</button>
463
+ <button>Load</button>
464
+ <button>Export</button>
465
+ </div>
466
+ )
467
+ }
468
+
469
+ @panel('bottom-right')
470
+ renderStatus() {
471
+ return (
472
+ <div className="status">
473
+ Ready
474
+ </div>
475
+ )
476
+ }
477
+ }
478
+ ```
479
+
480
+ **参数**:
481
+ - `position: Position` - 面板位置('top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right')
482
+ - `offset?: PanelOffset` - 面板偏移
483
+ - `x?: number` - X 轴偏移
484
+ - `y?: number` - Y 轴偏移
485
+
486
+ ## 文件目录结构
487
+
488
+ ```
489
+ src/
490
+ ├── index.ts # 主导出文件
491
+ ├── internal.ts # 内部工具函数
492
+ ├── edge/ # 边相关装饰器
493
+ │ ├── index.ts # 边模块导出
494
+ │ ├── edgeType.ts # 边类型装饰器
495
+ │ ├── edgeOperator.ts # 边操作装饰器
496
+ │ └── edgePipe.ts # 边管道装饰器
497
+ ├── engine/ # 引擎相关装饰器
498
+ │ ├── index.ts # 引擎模块导出
499
+ │ ├── inject/ # 注入装饰器
500
+ │ │ ├── index.ts # 注入模块导出
501
+ │ │ ├── inject-data.ts # 数据注入实现
502
+ │ │ ├── inject-decorator.ts # 注入装饰器实现
503
+ │ │ ├── inject-method.ts # 方法注入实现
504
+ │ │ ├── inject-plugin.ts # 插件注入实现
505
+ │ │ ├── injectable.ts # 可注入接口
506
+ │ │ ├── injectable-proxy.ts # 注入代理实现
507
+ │ │ ├── proxy.ts # 代理对象
508
+ │ │ └── types.ts # 类型定义
509
+ │ └── subscribe/ # 订阅装饰器
510
+ │ ├── index.ts # 订阅模块导出
511
+ │ ├── proxy.ts # 订阅代理对象
512
+ │ ├── subscribe-data.ts # 数据订阅实现
513
+ │ ├── subscribe-decorator.ts # 订阅装饰器实现
514
+ │ ├── subscribe-plugin.ts # 插件订阅实现
515
+ │ └── types.ts # 类型定义
516
+ ├── layer/ # 层相关装饰器
517
+ │ ├── index.ts # 层模块导出
518
+ │ ├── layer.ts # 层装饰器
519
+ │ └── panel.tsx # 面板装饰器
520
+ ├── node/ # 节点相关装饰器
521
+ │ ├── index.ts # 节点模块导出
522
+ │ ├── nodeType.ts # 节点类型装饰器
523
+ │ ├── nodeOperator.ts # 节点操作装饰器
524
+ │ └── nodePipe.ts # 节点管道装饰器
525
+ └── plugin/ # 插件相关装饰器
526
+ ├── index.ts # 插件模块导出
527
+ ├── config.ts # 配置装饰器
528
+ ├── lifecycle.ts # 生命周期装饰器
529
+ ├── observable.ts # 响应式装饰器
530
+ ├── register.ts # 注册装饰器
531
+ └── tool.ts # 工具函数装饰器
532
+ ```
533
+
534
+ ## 依赖关系
535
+
536
+ ### 对等依赖 (Peer Dependencies)
537
+
538
+ - `@knotx/jsx` - Knotx JSX 运行时支持
539
+
540
+ ### 主要依赖 (Dependencies)
541
+
542
+ - `@knotx/core` - Knotx 核心库
543
+ - `jsonschema` - JSON Schema 验证库
544
+ - `lodash-es` - 实用工具函数库
545
+ - `rxjs` - 响应式编程库
546
+
547
+ ## 许可证
548
+
549
+ MIT License
550
+
551
+ ## 贡献指南
552
+
553
+ 欢迎提交 Issue 和 Pull Request。在提交代码前,请确保:
554
+
555
+ 1. 代码符合项目的 ESLint 规则
556
+ 2. 通过 TypeScript 类型检查
557
+ 3. 添加必要的测试用例
558
+ 4. 更新相关文档
559
+
560
+ ## 更多信息
561
+
562
+ - [Knotx 官网](https://github.com/boenfu/knotx)
563
+ - [API 文档](https://github.com/boenfu/knotx#readme)
564
+ - [示例项目](https://github.com/boenfu/knotx/tree/main/apps/playground)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knotx/decorators",
3
- "version": "0.4.12",
3
+ "version": "0.4.13",
4
4
  "description": "Decorators for Knotx",
5
5
  "author": "boenfu",
6
6
  "license": "MIT",
@@ -28,20 +28,20 @@
28
28
  "dist"
29
29
  ],
30
30
  "peerDependencies": {
31
- "@knotx/jsx": "0.4.12"
31
+ "@knotx/jsx": "0.4.13"
32
32
  },
33
33
  "dependencies": {
34
34
  "jsonschema": "^1.5.0",
35
35
  "lodash-es": "^4.17.21",
36
36
  "rxjs": "^7.8.1",
37
- "@knotx/core": "0.4.12"
37
+ "@knotx/core": "0.4.13"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/lodash-es": "^4.14.12",
41
- "@knotx/build-config": "0.4.12",
42
- "@knotx/eslint-config": "0.4.12",
43
- "@knotx/jsx": "0.4.12",
44
- "@knotx/typescript-config": "0.4.12"
41
+ "@knotx/build-config": "0.4.13",
42
+ "@knotx/eslint-config": "0.4.13",
43
+ "@knotx/jsx": "0.4.13",
44
+ "@knotx/typescript-config": "0.4.13"
45
45
  },
46
46
  "scripts": {
47
47
  "build": "unbuild",