@libs-ui/components-draw-line 0.2.355-13 → 0.2.355-15

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.
package/README.md CHANGED
@@ -1,3 +1,436 @@
1
- # draw-line
1
+ # @libs-ui/components-draw-line
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ > Directive vẽ đường SVG nối giữa các điểm với nhiều chế độ đường cong, hỗ trợ né obstacle và kéo thả mũi tên.
4
+
5
+ ## Giới thiệu
6
+
7
+ `LibsUiComponentsDrawLineDirective` là một standalone Angular directive dùng để vẽ các đường nối SVG giữa hai điểm với nhiều chế độ vẽ khác nhau (quart-in, horizontal, vertical, horizontal-single-curve, vertical-single-curve). Directive hỗ trợ tự động né vật cản (obstacle avoidance), kéo thả điểm đầu mũi tên và xác định vùng có thể kết nối (reachable point range).
8
+
9
+ ### Tính năng
10
+
11
+ - ✅ 5 chế độ vẽ đường: quart-in, horizontal, vertical, horizontal-single-curve, vertical-single-curve
12
+ - ✅ Tự động né vật cản (obstacle avoidance) với obstacleRect
13
+ - ✅ Kéo thả mũi tên endpoint bằng chuột (drag-and-drop)
14
+ - ✅ Vùng có thể kết nối (reachable point range) với sự kiện khi kết nối thành công
15
+ - ✅ Tùy chỉnh style đường (stroke, dash, width), mũi tên, và circle
16
+ - ✅ Tự động tính toán viewBox cho SVG
17
+ - ✅ Hỗ trợ separatedPoints cho đường nối phức tạp
18
+ - ✅ Debug mode hiển thị rect/circle trực quan
19
+ - ✅ Chạy ngoài NgZone để tối ưu hiệu năng
20
+
21
+ ## Khi nào sử dụng
22
+
23
+ - Khi cần vẽ đường nối giữa các node trong flowchart, diagram, workflow editor
24
+ - Khi cần hiển thị mối quan hệ giữa các phần tử trên giao diện
25
+ - Khi cần cho phép người dùng kéo thả để tạo kết nối giữa các phần tử
26
+ - Khi cần vẽ đường cong phức tạp né tránh các vật cản trên canvas
27
+ - Phù hợp cho automation builder, mind map, ERD diagram, pipeline editor
28
+
29
+ ## Cài đặt
30
+
31
+ ```bash
32
+ # npm
33
+ npm install @libs-ui/components-draw-line
34
+
35
+ # yarn
36
+ yarn add @libs-ui/components-draw-line
37
+ ```
38
+
39
+ ## Import
40
+
41
+ ```typescript
42
+ import {
43
+ LibsUiComponentsDrawLineDirective,
44
+ IMoDrawLineFunctionControl,
45
+ IDrawLineDataInput,
46
+ IReachablePointRange,
47
+ IViewBoxConfig,
48
+ TYPE_MODE,
49
+ } from '@libs-ui/components-draw-line';
50
+
51
+ @Component({
52
+ standalone: true,
53
+ imports: [LibsUiComponentsDrawLineDirective],
54
+ // ...
55
+ })
56
+ export class YourComponent {}
57
+ ```
58
+
59
+ ## Ví dụ
60
+
61
+ ### Basic — Vẽ đường nối đơn giản
62
+
63
+ ```html
64
+ <div
65
+ LibsUiComponentsDrawLineDirective
66
+ (outDrawLineFunctionControl)="onFunctionControl($event)">
67
+ </div>
68
+ ```
69
+
70
+ ```typescript
71
+ import { LibsUiComponentsDrawLineDirective, IMoDrawLineFunctionControl, IDrawLineDataInput } from '@libs-ui/components-draw-line';
72
+
73
+ @Component({
74
+ standalone: true,
75
+ imports: [LibsUiComponentsDrawLineDirective],
76
+ // ...
77
+ })
78
+ export class BasicDrawLineComponent {
79
+ private fnControl!: IMoDrawLineFunctionControl;
80
+
81
+ onFunctionControl(ctrl: IMoDrawLineFunctionControl): void {
82
+ this.fnControl = ctrl;
83
+
84
+ const lines: IDrawLineDataInput[] = [
85
+ {
86
+ id: 'line-1',
87
+ mode: 'quart-in',
88
+ points: {
89
+ start: { x: 100, y: 100 },
90
+ end: { x: 300, y: 200 },
91
+ },
92
+ },
93
+ ];
94
+
95
+ this.fnControl.setData(lines);
96
+ }
97
+ }
98
+ ```
99
+
100
+ ### With Controls — Sử dụng FunctionsControl
101
+
102
+ ```typescript
103
+ export class ControlDrawLineComponent {
104
+ private fnControl!: IMoDrawLineFunctionControl;
105
+
106
+ onFunctionControl(ctrl: IMoDrawLineFunctionControl): void {
107
+ this.fnControl = ctrl;
108
+ }
109
+
110
+ addLine(): void {
111
+ this.fnControl.setData([
112
+ {
113
+ id: 'line-horizontal',
114
+ mode: 'horizontal',
115
+ points: {
116
+ start: { x: 50, y: 150 },
117
+ end: { x: 400, y: 300 },
118
+ },
119
+ lineStyle: {
120
+ stroke: '#3B82F6',
121
+ strokeWidth: '2px',
122
+ curve: 15,
123
+ },
124
+ arrowStyle: {
125
+ fill: '#3B82F6',
126
+ stroke: '#3B82F6',
127
+ width: 5,
128
+ height: 8,
129
+ },
130
+ },
131
+ ]);
132
+ }
133
+
134
+ removeLine(): void {
135
+ this.fnControl.removeLine('line-horizontal');
136
+ }
137
+
138
+ updateViewBox(): void {
139
+ this.fnControl.updateViewBox({
140
+ minX: 0,
141
+ minY: 0,
142
+ width: 800,
143
+ height: 600,
144
+ });
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### Obstacle Avoidance — Né vật cản
150
+
151
+ ```typescript
152
+ export class ObstacleDrawLineComponent {
153
+ private fnControl!: IMoDrawLineFunctionControl;
154
+
155
+ onFunctionControl(ctrl: IMoDrawLineFunctionControl): void {
156
+ this.fnControl = ctrl;
157
+
158
+ this.fnControl.setData([
159
+ {
160
+ id: 'line-obstacle',
161
+ mode: 'horizontal',
162
+ points: {
163
+ start: { x: 50, y: 100 },
164
+ end: { x: 500, y: 100 },
165
+ obstacleRect: [
166
+ { id: 'box-1', x: 200, y: 50, width: 100, height: 100 },
167
+ ],
168
+ },
169
+ lineStyle: { stroke: '#EF4444', strokeWidth: '2px' },
170
+ },
171
+ ]);
172
+ }
173
+ }
174
+ ```
175
+
176
+ ### Reachable Point Range — Vùng có thể kết nối
177
+
178
+ ```html
179
+ <div
180
+ LibsUiComponentsDrawLineDirective
181
+ (outDrawLineFunctionControl)="onFunctionControl($event)"
182
+ (outConnected)="onConnected($event)">
183
+ </div>
184
+ ```
185
+
186
+ ```typescript
187
+ export class ReachableDrawLineComponent {
188
+ private fnControl!: IMoDrawLineFunctionControl;
189
+
190
+ onFunctionControl(ctrl: IMoDrawLineFunctionControl): void {
191
+ this.fnControl = ctrl;
192
+
193
+ this.fnControl.setReachablePointRange([
194
+ {
195
+ id: 'target-1',
196
+ x: 400,
197
+ y: 200,
198
+ style: { r: 6, fill: '#10B981', stroke: '#059669', strokeWidth: '2px' },
199
+ },
200
+ ]);
201
+
202
+ this.fnControl.setData([
203
+ {
204
+ id: 'drag-line',
205
+ mode: 'quart-in',
206
+ endCircle: true,
207
+ endCircleStyle: { r: 4, fill: '#3B82F6' },
208
+ points: {
209
+ start: { x: 100, y: 200 },
210
+ end: { x: 250, y: 200 },
211
+ },
212
+ },
213
+ ]);
214
+ }
215
+
216
+ onConnected(event: { dataLine: any; dataReachablePointRange: any }): void {
217
+ console.log('Kết nối thành công:', event);
218
+ }
219
+ }
220
+ ```
221
+
222
+ ## API
223
+
224
+ ### [LibsUiComponentsDrawLineDirective]
225
+
226
+ #### Inputs
227
+
228
+ | Property | Type | Default | Description |
229
+ | ----------------- | ----------------- | ------------ | ------------------------------------------------ |
230
+ | `[drawRectDebug]` | `boolean` | `false` | Hiển thị debug rect/circle cho obstacle trên SVG |
231
+ | `[svgElement]` | `SVGSVGElement` | auto-created | SVG element để vẽ, tự tạo nếu không truyền vào |
232
+ | `[viewBoxConfig]` | `IViewBoxConfig` | `undefined` | Cấu hình viewBox cho SVG element |
233
+
234
+ #### Outputs
235
+
236
+ | Property | Type | Description |
237
+ | ------------------------------ | ----------------------------------------------------------- | -------------------------------------------------------------- |
238
+ | `(outConnected)` | `{ dataLine: IDrawLineDataInput, dataReachablePointRange: IReachablePointRange }` | Emit khi kéo mũi tên đến reachable point thành công |
239
+ | `(outDrawLineFunctionControl)` | `IMoDrawLineFunctionControl` | Emit object chứa các methods điều khiển draw-line |
240
+
241
+ #### FunctionsControl Methods
242
+
243
+ | Method | Signature | Description |
244
+ | -------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------- |
245
+ | `removeLine` | `(id: string, points?: Array<IPoints>) => void` | Xóa đường nối theo id, có thể chỉ xóa một số points |
246
+ | `removeReachablePointRange`| `(ids: Array<string>) => void` | Xóa các reachable point range theo danh sách id |
247
+ | `setData` | `(data: Array<IDrawLineDataInput>) => void` | Thêm dữ liệu đường nối để vẽ |
248
+ | `setReachablePointRange` | `(data: Array<IReachablePointRange>) => void` | Thêm các vùng có thể kết nối (target points) |
249
+ | `updateViewBox` | `(viewBoxConfig?: IViewBoxConfig) => void` | Cập nhật viewBox cho SVG element |
250
+
251
+ ## Types & Interfaces
252
+
253
+ ```typescript
254
+ /**
255
+ * Chế độ vẽ đường
256
+ */
257
+ export type TYPE_MODE =
258
+ | 'quart-in'
259
+ | 'vertical'
260
+ | 'vertical-single-curve'
261
+ | 'horizontal'
262
+ | 'horizontal-single-curve';
263
+
264
+ /**
265
+ * Hướng mũi tên
266
+ */
267
+ export type TYPE_DIRECTION = 'right' | 'left' | 'top' | 'bottom';
268
+
269
+ /**
270
+ * Dữ liệu đầu vào cho mỗi đường nối
271
+ */
272
+ export interface IDrawLineDataInput {
273
+ id: string;
274
+ points: IPoints;
275
+ mode: TYPE_MODE;
276
+ idConnected?: string;
277
+ lineStyle?: ILineStyle;
278
+ arrowStyle?: IArrowStyle;
279
+ arrowDirection?: TYPE_DIRECTION;
280
+ ignoreDrawArrow?: boolean;
281
+ startCircle?: boolean;
282
+ startCircleStyle?: ICircleStyle;
283
+ endCircle?: boolean;
284
+ endCircleStyle?: ICircleStyle;
285
+ isRemove?: boolean;
286
+ ref?: IDrawLineDataInput;
287
+ startEndMode?: 'right-left' | 'bottom-top' | 'bottom-left';
288
+ }
289
+
290
+ /**
291
+ * Tọa độ điểm đầu và điểm cuối
292
+ */
293
+ export interface IPoints {
294
+ start: IPoint;
295
+ end: IPoint;
296
+ initialized?: boolean;
297
+ obstacleRect?: Array<IRect>;
298
+ separatedPoints?: Array<IPoints>;
299
+ pathElement?: SVGPathElement;
300
+ arrowElement?: SVGPolylineElement;
301
+ circleStartElement?: SVGCircleElement;
302
+ circleEndElement?: SVGCircleElement;
303
+ onDestroyEvent?: Subject<void>;
304
+ mode?: TYPE_MODE;
305
+ id?: string;
306
+ name?: string;
307
+ }
308
+
309
+ /**
310
+ * Tọa độ một điểm
311
+ */
312
+ export interface IPoint {
313
+ x: number;
314
+ y: number;
315
+ }
316
+
317
+ /**
318
+ * Vùng có thể kết nối
319
+ */
320
+ export interface IReachablePointRange {
321
+ id: string;
322
+ x: number;
323
+ y: number;
324
+ style: ICircleStyle;
325
+ idConnected?: string;
326
+ ref?: IReachablePointRange;
327
+ element?: SVGCircleElement;
328
+ onDestroyEvent?: Subject<void>;
329
+ }
330
+
331
+ /**
332
+ * Cấu hình viewBox cho SVG
333
+ */
334
+ export interface IViewBoxConfig {
335
+ minX?: number;
336
+ minY?: number;
337
+ width?: number;
338
+ height?: number;
339
+ ignoreViewBox?: boolean;
340
+ marginBetweenElement?: number;
341
+ }
342
+
343
+ /**
344
+ * Style cho đường nối
345
+ */
346
+ export interface ILineStyle {
347
+ stroke?: string;
348
+ strokeDasharray?: string;
349
+ strokeWidth?: string;
350
+ fill?: string;
351
+ curve?: number;
352
+ distancePoint?: number;
353
+ }
354
+
355
+ /**
356
+ * Style cho mũi tên
357
+ */
358
+ export interface IArrowStyle {
359
+ stroke?: string;
360
+ strokeWidth?: string;
361
+ width?: number;
362
+ height?: number;
363
+ fill?: string;
364
+ }
365
+
366
+ /**
367
+ * Style cho circle (điểm tròn)
368
+ */
369
+ export interface ICircleStyle {
370
+ stroke?: string;
371
+ strokeWidth?: string;
372
+ fill?: string;
373
+ r?: number;
374
+ }
375
+
376
+ /**
377
+ * Vùng chữ nhật (obstacle / debug rect)
378
+ */
379
+ export interface IRect {
380
+ id?: string;
381
+ x: number;
382
+ y: number;
383
+ width: number;
384
+ height: number;
385
+ gapXObstacleRect?: number;
386
+ gapYObstacleRect?: number;
387
+ }
388
+
389
+ /**
390
+ * Object điều khiển draw-line
391
+ */
392
+ export interface IMoDrawLineFunctionControl {
393
+ setData: (data: Array<IDrawLineDataInput>) => void;
394
+ removeLine: (id: string, points?: Array<IPoints>) => void;
395
+ setReachablePointRange: (data: Array<IReachablePointRange>) => void;
396
+ removeReachablePointRange: (ids: Array<string>) => void;
397
+ updateViewBox: (viewBoxConfig?: IViewBoxConfig) => void;
398
+ }
399
+ ```
400
+
401
+ ## Công nghệ
402
+
403
+ | Technology | Version | Purpose |
404
+ | ---------- | ------- | --------------------- |
405
+ | Angular | ≥18.0.0 | Framework |
406
+ | @libs-ui/utils | 0.2.355-14 | Utilities (cloneDeep, getDragEventByElement, checkMouseOverInContainer) |
407
+ | RxJS | ~7.8.0 | Reactive programming |
408
+ | SVG | - | Rendering đường nối |
409
+
410
+ ## Demo
411
+
412
+ ```bash
413
+ npx nx serve core-ui
414
+ ```
415
+
416
+ Truy cập: `http://localhost:4500/draw-line`
417
+
418
+ ## Kiểm thử
419
+
420
+ ```bash
421
+ # Chạy tests
422
+ nx test draw-line
423
+
424
+ # Lint
425
+ nx lint draw-line
426
+
427
+ # Coverage
428
+ nx test draw-line --coverage
429
+
430
+ # Watch mode
431
+ nx test draw-line --watch
432
+ ```
433
+
434
+ ## License
435
+
436
+ MIT