@krainovsd/graph 0.4.5 → 0.6.0

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 (48) hide show
  1. package/lib/cjs/index.cjs +1144 -0
  2. package/lib/cjs/index.cjs.map +1 -0
  3. package/lib/esm/index.js +12 -935
  4. package/lib/esm/index.js.map +1 -1
  5. package/lib/esm/lib/check-type.js +6 -0
  6. package/lib/esm/lib/check-type.js.map +1 -0
  7. package/lib/esm/lib/color-getter.js +28 -0
  8. package/lib/esm/lib/color-getter.js.map +1 -0
  9. package/lib/esm/lib/color-to-rgb.js +22 -0
  10. package/lib/esm/lib/color-to-rgb.js.map +1 -0
  11. package/lib/esm/lib/extract-rgb.js +15 -0
  12. package/lib/esm/lib/extract-rgb.js.map +1 -0
  13. package/lib/esm/lib/fade-rgb.js +11 -0
  14. package/lib/esm/lib/fade-rgb.js.map +1 -0
  15. package/lib/esm/lib/rgb-animation-by-progress.js +10 -0
  16. package/lib/esm/lib/rgb-animation-by-progress.js.map +1 -0
  17. package/lib/esm/module/GraphCanvas/GraphCanvas.js +731 -0
  18. package/lib/esm/module/GraphCanvas/GraphCanvas.js.map +1 -0
  19. package/lib/esm/module/GraphCanvas/constants/settings.js +95 -0
  20. package/lib/esm/module/GraphCanvas/constants/settings.js.map +1 -0
  21. package/lib/esm/module/GraphCanvas/lib/settings/force-settings-getter.js +11 -0
  22. package/lib/esm/module/GraphCanvas/lib/settings/force-settings-getter.js.map +1 -0
  23. package/lib/esm/module/GraphCanvas/lib/settings/graph-settings-getter.js +11 -0
  24. package/lib/esm/module/GraphCanvas/lib/settings/graph-settings-getter.js.map +1 -0
  25. package/lib/esm/module/GraphCanvas/lib/settings/link-settings-getter.js +19 -0
  26. package/lib/esm/module/GraphCanvas/lib/settings/link-settings-getter.js.map +1 -0
  27. package/lib/esm/module/GraphCanvas/lib/settings/listeners-getter.js +6 -0
  28. package/lib/esm/module/GraphCanvas/lib/settings/listeners-getter.js.map +1 -0
  29. package/lib/esm/module/GraphCanvas/lib/settings/node-settings-getter.js +53 -0
  30. package/lib/esm/module/GraphCanvas/lib/settings/node-settings-getter.js.map +1 -0
  31. package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-radius.js +20 -0
  32. package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-radius.js.map +1 -0
  33. package/lib/esm/module/GraphCanvas/lib/utils/drag-place-coefficient-getter.js +11 -0
  34. package/lib/esm/module/GraphCanvas/lib/utils/drag-place-coefficient-getter.js.map +1 -0
  35. package/lib/esm/module/GraphCanvas/lib/utils/draw-text.js +47 -0
  36. package/lib/esm/module/GraphCanvas/lib/utils/draw-text.js.map +1 -0
  37. package/lib/esm/module/GraphCanvas/lib/utils/is-overlaps-node.js +10 -0
  38. package/lib/esm/module/GraphCanvas/lib/utils/is-overlaps-node.js.map +1 -0
  39. package/lib/esm/module/GraphCanvas/lib/utils/link-iteration-extractor.js +37 -0
  40. package/lib/esm/module/GraphCanvas/lib/utils/link-iteration-extractor.js.map +1 -0
  41. package/lib/esm/module/GraphCanvas/lib/utils/node-by-pointer-getter.js +26 -0
  42. package/lib/esm/module/GraphCanvas/lib/utils/node-by-pointer-getter.js.map +1 -0
  43. package/lib/esm/module/GraphCanvas/lib/utils/node-iteration-extractor.js +37 -0
  44. package/lib/esm/module/GraphCanvas/lib/utils/node-iteration-extractor.js.map +1 -0
  45. package/lib/esm/module/GraphCanvas/lib/utils/pointer-getter.js +8 -0
  46. package/lib/esm/module/GraphCanvas/lib/utils/pointer-getter.js.map +1 -0
  47. package/lib/index.d.ts +135 -29
  48. package/package.json +20 -28
package/lib/esm/index.js CHANGED
@@ -1,936 +1,13 @@
1
- import { greatest } from 'd3-array';
2
- import { drag } from 'd3-drag';
3
- import { forceLink, forceSimulation, forceX, forceY, forceManyBody, forceCenter, forceCollide } from 'd3-force';
4
- import { create, select } from 'd3-selection';
5
- import { zoomIdentity, zoom, ZoomTransform } from 'd3-zoom';
6
-
7
- const FORCE_SETTINGS = {
8
- centerPosition: {},
9
- centerStrength: 1,
10
- collideStrength: 0.1,
11
- collideAdditionalRadius: 4,
12
- collideIterations: 2,
13
- collideOffMax: { links: 0, nodes: 0 },
14
- collideOn: true,
15
- chargeStrength: -40,
16
- chargeDistanceMax: Infinity,
17
- chargeDistanceMin: 1,
18
- xForce: 0,
19
- xStrength: 0.1,
20
- yForce: 0,
21
- yStrength: 0.1,
22
- linkDistance: 30,
23
- linkIterations: 1,
24
- linkStrength: 1,
25
- collideRadius: null,
26
- };
27
- const GRAPH_SETTINGS = {
28
- zoomExtent: [0.1, 20],
29
- stickAfterDrag: false,
30
- highlightByHover: false,
31
- highlightFadingMin: 0.21,
32
- highlightDownStep: 0.09,
33
- highlightUpStep: 0.09,
34
- dragPlaceCoefficient: dragPlaceCoefficientGetter,
35
- nodeRadiusInitial: 4,
36
- nodeRadiusCoefficient: 5,
37
- nodeRadiusFactor: 1,
38
- nodeRadiusFlexible: true,
39
- zoomInitial: null,
40
- };
41
- const NODE_SETTINGS = {
42
- alpha: 1,
43
- borderColor: "#000000FF",
44
- borderWidth: 0.1,
45
- textWidth: 20,
46
- textShiftX: 0,
47
- textFont: "Arial",
48
- textAlign: "center",
49
- textColor: "#333",
50
- width: 1,
51
- radius: 4,
52
- textStyle: "normal",
53
- textWeight: "500",
54
- textGap: 1,
55
- };
56
- const LINK_SETTINGS = {
57
- alpha: 1,
58
- };
59
- const COMMON_SETTINGS = {
60
- linkColorZoomFar: "#999",
61
- linkColorZoomNear: "#000000FF",
62
- linkWidthZoomFar: 1,
63
- linkWidthZoomNear: 0.1,
64
- linkWidthZoomBorder: 1,
65
- linkColorZoomBorder: 1,
66
- nodeTextScaleMin: 1.5,
67
- nodeTextScaleMax: 20,
68
- nodeTextSizeMin: 1.5,
69
- nodeTextSizeMax: 3.5,
70
- nodeTextShiftYMin: 2.5,
71
- nodeTextShiftYMax: 4,
72
- nodeTextChangeStepCount: 200,
73
- };
74
-
75
- function forceSettingsGetter(settings, prevSettings) {
76
- return {
77
- ...(prevSettings ?? FORCE_SETTINGS),
78
- ...settings,
79
- };
80
- }
81
-
82
- function graphSettingsGetter(settings, prevSettings) {
83
- return {
84
- ...(prevSettings ?? GRAPH_SETTINGS),
85
- ...settings,
86
- };
87
- }
88
-
89
- function linkSettingsGetter(settings) {
90
- return { options: settings?.options };
91
- }
92
- function linkOptionsGetter(_, __, ___, transform) {
93
- return {
94
- ...LINK_SETTINGS,
95
- color: transform && transform.k > COMMON_SETTINGS.linkColorZoomBorder
96
- ? COMMON_SETTINGS.linkColorZoomNear
97
- : COMMON_SETTINGS.linkColorZoomFar,
98
- width: transform && transform.k > COMMON_SETTINGS.linkWidthZoomBorder
99
- ? COMMON_SETTINGS.linkWidthZoomNear
100
- : COMMON_SETTINGS.linkWidthZoomFar,
101
- };
102
- }
103
-
104
- function listenersGetter(settings) {
105
- return settings || {};
106
- }
107
-
108
- function checkType(value, condition) {
109
- return condition;
110
- }
111
-
112
- function colorGetter() {
113
- const chosenColors = {};
114
- const colors = [
115
- "#1f77b4",
116
- "#ff7f0e",
117
- "#2ca02c",
118
- "#d62728",
119
- "#9467bd",
120
- "#8c564b",
121
- "#e377c2",
122
- "#7f7f7f",
123
- "#bcbd22",
124
- "#17becf",
125
- ];
126
- let cursor = 0;
127
- return function color(key) {
128
- if (chosenColors[key])
129
- return chosenColors[key];
130
- chosenColors[key] = colors[cursor];
131
- cursor++;
132
- if (cursor >= colors.length)
133
- cursor = 0;
134
- return chosenColors[key];
135
- };
136
- }
137
-
138
- function nodeSettingsGetter(settings) {
139
- return {
140
- idGetter: settings?.idGetter ?? nodeIdGetter,
141
- options: settings?.options,
142
- };
143
- }
144
- const color = colorGetter();
145
- function nodeOptionsGetter(node, _, __, transform) {
146
- const { textShiftY, textSize } = nodeTextSizeGetter(transform);
147
- return {
148
- ...NODE_SETTINGS,
149
- color: color(String(node.group || "_DEFAULT")),
150
- textVisible: Boolean(transform && transform.k > COMMON_SETTINGS.nodeTextScaleMin),
151
- text: node.id != undefined ? String(node.id) : null,
152
- textShiftY,
153
- textSize,
154
- };
155
- }
156
- function nodeTextSizeGetter(transform) {
157
- let textSize = COMMON_SETTINGS.nodeTextSizeMax;
158
- let textShiftY = COMMON_SETTINGS.nodeTextShiftYMax;
159
- if (transform) {
160
- const scaleStepCoefficient = (COMMON_SETTINGS.nodeTextScaleMax - COMMON_SETTINGS.nodeTextScaleMin) /
161
- COMMON_SETTINGS.nodeTextChangeStepCount;
162
- const textStepCoefficient = (COMMON_SETTINGS.nodeTextSizeMax - COMMON_SETTINGS.nodeTextSizeMin) /
163
- COMMON_SETTINGS.nodeTextChangeStepCount;
164
- const shiftStepCoefficient = (COMMON_SETTINGS.nodeTextShiftYMax - COMMON_SETTINGS.nodeTextShiftYMin) /
165
- COMMON_SETTINGS.nodeTextChangeStepCount;
166
- if (transform.k >= COMMON_SETTINGS.nodeTextScaleMax) {
167
- textSize = COMMON_SETTINGS.nodeTextSizeMin;
168
- textShiftY = COMMON_SETTINGS.nodeTextShiftYMin;
169
- }
170
- else if (transform.k > COMMON_SETTINGS.nodeTextScaleMin) {
171
- const transformSteps = (transform.k - COMMON_SETTINGS.nodeTextScaleMin) / scaleStepCoefficient;
172
- textSize -= transformSteps * textStepCoefficient;
173
- textShiftY -= transformSteps * shiftStepCoefficient;
174
- }
175
- }
176
- return { textSize, textShiftY };
177
- }
178
- function nodeRadiusGetter({ radiusFlexible, radiusInitial, linkCount, radiusCoefficient, radiusFactor, }) {
179
- return ((radiusFlexible && linkCount ? linkCount / radiusCoefficient : 0) * radiusFactor + radiusInitial);
180
- }
181
- function nodeIdGetter(d) {
182
- return d.id;
183
- }
184
-
185
- function pointerGetter(mouseEvent, areaRect, areaTransform) {
186
- const px = (mouseEvent.clientX - areaRect.left - areaTransform.x) / areaTransform.k;
187
- const py = (mouseEvent.clientY - areaRect.top - areaTransform.y) / areaTransform.k;
188
- return [px, py];
189
- }
190
-
191
- function isOverlapsNode(node, radius, pointerX, pointerY) {
192
- if (node.x == undefined || node.y == undefined)
193
- return false;
194
- const isOverX = node.x - radius <= pointerX && pointerX <= node.x + radius;
195
- const isOverY = node.y - radius <= pointerY && pointerY <= node.y + radius;
196
- return isOverX && isOverY;
197
- }
198
-
199
- function dragPlaceCoefficientGetter(node, pointerX, pointerY, radius) {
200
- if (!node.x || !node.y)
201
- return undefined;
202
- if (isOverlapsNode(node, radius, pointerX, pointerY))
203
- return node.index;
204
- }
205
-
206
- function linkIterationExtractor(link, i, links, transform, option, optionConstantGetter) {
207
- let customOptions;
208
- let constantOptions;
209
- if (typeof option === "function")
210
- customOptions = option(link, i, links, transform);
211
- else
212
- customOptions = option;
213
- if (customOptions && typeof customOptions === "object" && !Array.isArray(customOptions)) {
214
- for (const key in customOptions) {
215
- if (customOptions[key] === undefined)
216
- delete customOptions[key];
217
- }
218
- }
219
- if (optionConstantGetter) {
220
- if (typeof optionConstantGetter === "function")
221
- constantOptions = optionConstantGetter(link, i, links, transform);
222
- else
223
- constantOptions = optionConstantGetter;
224
- if (constantOptions &&
225
- typeof constantOptions === "object" &&
226
- !Array.isArray(constantOptions) &&
227
- checkType(customOptions, customOptions === undefined ||
228
- (typeof customOptions === "object" && !Array.isArray(constantOptions)))) {
229
- return {
230
- ...constantOptions,
231
- ...(customOptions || {}),
232
- };
233
- }
234
- }
235
- return customOptions;
236
- }
237
-
238
- function nodeByPointerGetter({ areaRect, areaTransform, mouseEvent, nodes, graphSettings, }) {
239
- if (!areaRect)
240
- return undefined;
241
- const [pointerX, pointerY] = pointerGetter(mouseEvent, areaRect, areaTransform);
242
- return greatest(nodes, (node) => {
243
- const radius = nodeRadiusGetter({
244
- radiusFlexible: graphSettings.nodeRadiusFlexible,
245
- radiusInitial: graphSettings.nodeRadiusInitial,
246
- radiusCoefficient: graphSettings.nodeRadiusCoefficient,
247
- radiusFactor: graphSettings.nodeRadiusFactor,
248
- linkCount: node.linkCount,
249
- });
250
- if (isOverlapsNode(node, radius, pointerX, pointerY))
251
- return node.index;
252
- });
253
- }
254
-
255
- function nodeIterationExtractor(node, i, nodes, transform, option, optionConstantGetter) {
256
- let customOptions;
257
- let constantOptions;
258
- if (typeof option === "function")
259
- customOptions = option(node, i, nodes, transform);
260
- else
261
- customOptions = option;
262
- if (customOptions && typeof customOptions === "object" && !Array.isArray(customOptions)) {
263
- for (const key in customOptions) {
264
- if (customOptions[key] === undefined)
265
- delete customOptions[key];
266
- }
267
- }
268
- if (optionConstantGetter) {
269
- if (typeof optionConstantGetter === "function")
270
- constantOptions = optionConstantGetter(node, i, nodes, transform);
271
- else
272
- constantOptions = optionConstantGetter;
273
- if (constantOptions &&
274
- typeof constantOptions === "object" &&
275
- !Array.isArray(constantOptions) &&
276
- checkType(customOptions, customOptions === undefined ||
277
- (typeof customOptions === "object" && !Array.isArray(constantOptions)))) {
278
- return {
279
- ...constantOptions,
280
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
281
- ...(customOptions || {}),
282
- };
283
- }
284
- }
285
- return customOptions;
286
- }
287
-
288
- const SPACE = " ";
289
- function drawText({ context, id, textAlign, textColor, textFont, textStyle, textGap, textWeight, textSize, text, x, y, cachedNodeText, maxWidth, }) {
290
- context.font = `${textStyle} normal ${textWeight} ${textSize}px ${textFont}`;
291
- context.fillStyle = textColor;
292
- context.textAlign = textAlign;
293
- if (cachedNodeText[id] != undefined) {
294
- cachedNodeText[id].forEach((line, index) => {
295
- context.fillText(line, x, y + index * textSize + index * textGap);
296
- });
297
- return;
298
- }
299
- if (maxWidth == undefined || context.measureText(text).width <= maxWidth) {
300
- cachedNodeText[id] = [text];
301
- context.fillText(text, x, y);
302
- return;
303
- }
304
- const spaceWidth = context.measureText(" ").width;
305
- const lines = [];
306
- let lineWidth = 0;
307
- let line = "";
308
- for (const word of text.split(" ")) {
309
- const size = context.measureText(word).width;
310
- lineWidth += size;
311
- if (line === "") {
312
- line = word;
313
- continue;
314
- }
315
- if (lineWidth > maxWidth) {
316
- lineWidth = 0;
317
- lines.push(line);
318
- line = word;
319
- }
320
- else {
321
- lineWidth += spaceWidth;
322
- line += `${SPACE}${word}`;
323
- }
324
- }
325
- if (line !== "")
326
- lines.push(line);
327
- cachedNodeText[id] = lines;
328
- lines.forEach((line, index) => {
329
- context.fillText(line, x, y + index * textSize + index * textGap);
330
- });
331
- }
332
-
333
- class GraphCanvas {
334
- /** initial data */
335
- nodes;
336
- links;
337
- width;
338
- height;
339
- root;
340
- container;
341
- area;
342
- /** settings */
343
- graphSettings;
344
- forceSettings;
345
- nodeSettings;
346
- linkSettings;
347
- listeners;
348
- /** service */
349
- context;
350
- simulation;
351
- areaTransform = zoomIdentity;
352
- areaRect;
353
- draw;
354
- eventAbortController;
355
- cachedNodeText = {};
356
- simulationWorking = false;
357
- isDragging = false;
358
- highlightedNode = null;
359
- highlightedNeighbors = null;
360
- highlighFading = 1;
361
- highlightFadingWorking = false;
362
- highlightDrawing = false;
363
- constructor({ links, nodes, root, forceSettings, linkSettings, listeners, nodeSettings, graphSettings, }) {
364
- // root.style.position = "relative";
365
- root.style.overflow = "hidden";
366
- this.root = root;
367
- this.forceSettings = forceSettingsGetter(forceSettings);
368
- this.linkSettings = linkSettingsGetter(linkSettings);
369
- this.nodeSettings = nodeSettingsGetter(nodeSettings);
370
- this.listeners = listenersGetter(listeners);
371
- this.graphSettings = graphSettingsGetter(graphSettings);
372
- this.eventAbortController = new AbortController();
373
- this.nodes = nodes;
374
- this.links = links;
375
- this.height = 0;
376
- this.width = 0;
377
- this.draw = this.initDraw();
378
- this.init();
379
- }
380
- get dpi() {
381
- return devicePixelRatio;
382
- }
383
- getData() {
384
- return {
385
- links: this.links,
386
- nodes: this.nodes,
387
- };
388
- }
389
- changeData(options, alpha) {
390
- if (options.links != undefined)
391
- this.links = options.links;
392
- if (options.nodes != undefined)
393
- this.nodes = options.nodes;
394
- if (options.nodes != undefined || options.links != undefined)
395
- this.updateData(alpha);
396
- }
397
- changeSettings(options) {
398
- if (options.graphSettings)
399
- this.graphSettings = graphSettingsGetter(options.graphSettings, this.graphSettings);
400
- if (options.forceSettings)
401
- this.forceSettings = forceSettingsGetter(options.forceSettings, this.forceSettings);
402
- if (options.linkSettings)
403
- this.linkSettings = linkSettingsGetter(options.linkSettings);
404
- if (options.nodeSettings)
405
- this.nodeSettings = nodeSettingsGetter(options.nodeSettings);
406
- if (options.forceSettings)
407
- return void this.updateSimulation();
408
- this.tick();
409
- }
410
- tick() {
411
- if (!this.simulationWorking)
412
- this.draw();
413
- }
414
- restart(alpha) {
415
- if (this.simulation)
416
- this.simulation.alpha(alpha ?? 1).restart();
417
- }
418
- start() {
419
- if (this.simulation)
420
- this.simulation.alpha(1).restart();
421
- if (this.container)
422
- this.container.style.display = "block";
423
- }
424
- stop() {
425
- if (this.simulation)
426
- this.simulation.stop();
427
- if (this.container)
428
- this.container.style.display = "none";
429
- }
430
- create() {
431
- this.init();
432
- }
433
- destroy() {
434
- if (this.simulation) {
435
- this.simulation.stop();
436
- this.simulation = undefined;
437
- }
438
- this.clearHTMLElements();
439
- this.clearState();
440
- this.clearDataDependencies();
441
- }
442
- clearHTMLElements() {
443
- this.root.replaceChildren();
444
- this.area = undefined;
445
- this.context = undefined;
446
- this.container = undefined;
447
- this.eventAbortController.abort();
448
- this.eventAbortController = new AbortController();
449
- }
450
- updateSimulation() {
451
- if (this.simulation) {
452
- this.initSimulationForces();
453
- this.simulation.alpha(1);
454
- this.simulation.restart();
455
- }
456
- }
457
- clearState() {
458
- this.isDragging = false;
459
- this.simulationWorking = false;
460
- this.highlightedNode = null;
461
- this.highlightedNeighbors = null;
462
- this.highlighFading = 1;
463
- this.highlightFadingWorking = false;
464
- this.highlightDrawing = false;
465
- }
466
- clearDataDependencies() {
467
- this.cachedNodeText = {};
468
- }
469
- updateData(alpha) {
470
- this.clearDataDependencies();
471
- if (this.simulation) {
472
- this.initCollideForce();
473
- this.simulation
474
- .nodes(this.nodes)
475
- .force("link", forceLink(this.links)
476
- .id(this.nodeSettings.idGetter)
477
- .distance(this.forceSettings.linkDistance)
478
- .strength(this.forceSettings.linkStrength)
479
- .iterations(this.forceSettings.linkIterations))
480
- .alpha(alpha ?? 0.5)
481
- .restart();
482
- }
483
- }
484
- updateSize() {
485
- this.clearHTMLElements();
486
- this.initArea();
487
- this.initDnd();
488
- this.initZoom();
489
- this.initResize();
490
- this.initPointer();
491
- if (!this.simulationWorking)
492
- this.draw();
493
- }
494
- init() {
495
- this.initArea();
496
- this.initSimulation();
497
- this.initDnd();
498
- this.initZoom();
499
- this.initResize();
500
- this.initPointer();
501
- }
502
- initSimulation() {
503
- if (!this.simulation) {
504
- this.simulation = forceSimulation()
505
- .nodes(this.nodes)
506
- .force("link", forceLink(this.links).id(this.nodeSettings.idGetter))
507
- .on("tick", () => {
508
- this.simulationWorking = true;
509
- this.draw();
510
- })
511
- .on("end", () => {
512
- this.listeners.onSimulationEnd?.(this.simulation);
513
- this.simulationWorking = false;
514
- });
515
- this.initSimulationForces();
516
- }
517
- }
518
- initSimulationForces() {
519
- if (!this.simulation)
520
- return;
521
- const linkForce = this.simulation.force("link");
522
- linkForce
523
- .distance(this.forceSettings.linkDistance)
524
- .strength(this.forceSettings.linkStrength)
525
- .iterations(this.forceSettings.linkIterations);
526
- this.simulation
527
- .force("x", forceX(this.forceSettings.xForce).strength(this.forceSettings.xStrength))
528
- .force("y", forceY(this.forceSettings.yForce).strength(this.forceSettings.yStrength))
529
- .force("charge", forceManyBody()
530
- .strength(this.forceSettings.chargeStrength)
531
- .distanceMax(this.forceSettings.chargeDistanceMax)
532
- .distanceMin(this.forceSettings.chargeDistanceMin))
533
- .force("center", forceCenter(this.forceSettings.centerPosition.x || 0, this.forceSettings.centerPosition.y || 0).strength(this.forceSettings.centerStrength));
534
- this.initCollideForce();
535
- }
536
- initCollideForce() {
537
- if (!this.simulation)
538
- return;
539
- if (!this.forceSettings.collideOn) {
540
- if (this.simulation.force("collide"))
541
- this.simulation.force("collide", null);
542
- return;
543
- }
544
- const isHasMax = this.forceSettings.collideOffMax.links != 0 && this.forceSettings.collideOffMax.nodes != 0;
545
- const isMaxCollideNodes = isHasMax && this.forceSettings.collideOffMax.nodes < this.nodes.length;
546
- const isMaxCollideLinks = isHasMax && this.forceSettings.collideOffMax.links < this.links.length;
547
- if (isMaxCollideNodes && isMaxCollideLinks)
548
- this.simulation.force("collide", null);
549
- else if (!this.simulation.force("collide"))
550
- this.simulation.force("collide", forceCollide()
551
- .radius((node, index) => {
552
- if (this.forceSettings.collideRadius) {
553
- return nodeIterationExtractor(node, index, this.nodes, this.areaTransform, this.forceSettings.collideIterations, undefined);
554
- }
555
- const nodeOptions = nodeIterationExtractor(node, index, this.nodes, this.areaTransform, this.nodeSettings.options || {}, nodeOptionsGetter);
556
- const radius = nodeRadiusGetter({
557
- radiusFlexible: this.graphSettings.nodeRadiusFlexible,
558
- radiusInitial: this.graphSettings.nodeRadiusInitial,
559
- radiusCoefficient: this.graphSettings.nodeRadiusCoefficient,
560
- radiusFactor: this.graphSettings.nodeRadiusFactor,
561
- linkCount: node.linkCount,
562
- }) ?? nodeOptions.radius;
563
- return radius + this.forceSettings.collideAdditionalRadius;
564
- })
565
- .strength(this.forceSettings.collideStrength)
566
- .iterations(this.forceSettings.collideIterations));
567
- }
568
- initArea() {
569
- if (!this.area || !this.context || !this.container) {
570
- this.container = create("div")
571
- .attr("style", "padding: 0 !important; width: 100%; height: 100%;")
572
- .node();
573
- if (!this.container)
574
- throw new Error("couldn't create container");
575
- this.root.appendChild(this.container);
576
- const { width, height } = this.root.getBoundingClientRect();
577
- this.width = width;
578
- this.height = height;
579
- this.area = create("canvas")
580
- .attr("width", this.dpi * this.width)
581
- .attr("height", this.dpi * this.height)
582
- .attr("style", `width: 100%; height: 100%; border: none !important;`)
583
- .node();
584
- if (!this.area)
585
- throw new Error("couldn't create canvas");
586
- this.container.appendChild(this.area);
587
- this.areaRect = this.area.getBoundingClientRect();
588
- this.context = this.area.getContext("2d");
589
- if (!this.context)
590
- throw new Error("couldn't create canvas context");
591
- this.context.scale(this.dpi, this.dpi);
592
- }
593
- }
594
- initDraw() {
595
- function calculateHighlightFading() {
596
- this.highlightDrawing = true;
597
- if (!this.highlightFadingWorking) {
598
- switch (true) {
599
- case this.highlighFading < 1: {
600
- this.highlighFading += this.graphSettings.highlightUpStep;
601
- break;
602
- }
603
- default: {
604
- if (this.highlightedNeighbors)
605
- this.highlightedNeighbors = null;
606
- if (this.highlightedNode)
607
- this.highlightedNode = null;
608
- break;
609
- }
610
- }
611
- if (this.highlighFading < 1 && !this.simulationWorking)
612
- return void requestAnimationFrame(() => this.draw());
613
- }
614
- if (this.highlightFadingWorking) {
615
- switch (true) {
616
- case this.highlighFading > this.graphSettings.highlightFadingMin: {
617
- this.highlighFading -= this.graphSettings.highlightDownStep;
618
- break;
619
- }
620
- }
621
- if (this.highlighFading > this.graphSettings.highlightFadingMin && !this.simulationWorking)
622
- return void requestAnimationFrame(() => this.draw());
623
- }
624
- this.highlightDrawing = false;
625
- }
626
- function draw() {
627
- if (!this.context)
628
- return;
629
- if (this.listeners.onDraw) {
630
- return void this.listeners.onDraw(this.context, this.areaTransform);
631
- }
632
- this.context.save();
633
- this.context.clearRect(0, 0, this.width, this.height);
634
- this.context.translate(this.areaTransform.x, this.areaTransform.y);
635
- this.context.scale(this.areaTransform.k, this.areaTransform.k);
636
- /** links */
637
- this.context.beginPath();
638
- this.links.forEach(drawLink.bind(this));
639
- this.context.stroke();
640
- /** nodes */
641
- const textRenders = [];
642
- this.nodes.forEach(drawNode(textRenders).bind(this));
643
- textRenders.forEach((r) => r());
644
- this.context.restore();
645
- this.listeners.onDrawFinished?.(this.context, this.areaTransform);
646
- calculateHighlightFading.bind(this)();
647
- }
648
- function drawLink(link, index) {
649
- if (!this.context ||
650
- typeof link.source !== "object" ||
651
- typeof link.target !== "object" ||
652
- !link.source.x ||
653
- !link.source.y ||
654
- !link.target.x ||
655
- !link.target.y)
656
- return;
657
- const linkOptions = linkIterationExtractor(link, index, this.links, this.areaTransform, this.linkSettings.options || {}, linkOptionsGetter);
658
- let alpha = linkOptions.alpha;
659
- if (this.highlightedNeighbors && this.highlightedNode) {
660
- if (this.highlightedNode.id != link.source.id &&
661
- this.highlightedNode.id != link.target.id) {
662
- alpha = this.highlighFading;
663
- }
664
- this.context.beginPath();
665
- }
666
- this.context.globalAlpha = alpha;
667
- this.context.strokeStyle = linkOptions.color;
668
- this.context.lineWidth = linkOptions.width;
669
- this.context.moveTo(link.source.x, link.source.y);
670
- this.context.lineTo(link.target.x, link.target.y);
671
- if (this.highlightedNeighbors && this.highlightedNode)
672
- this.context.stroke();
673
- }
674
- const drawNode = (textRenders) => function drawNode(node, index) {
675
- if (!this.context || !node.x || !node.y)
676
- return;
677
- const nodeOptions = nodeIterationExtractor(node, index, this.nodes, this.areaTransform, this.nodeSettings.options || {}, nodeOptionsGetter);
678
- const radius = nodeRadiusGetter({
679
- radiusFlexible: this.graphSettings.nodeRadiusFlexible,
680
- radiusInitial: this.graphSettings.nodeRadiusInitial,
681
- radiusCoefficient: this.graphSettings.nodeRadiusCoefficient,
682
- radiusFactor: this.graphSettings.nodeRadiusFactor,
683
- linkCount: node.linkCount,
684
- }) ?? nodeOptions.radius;
685
- let alpha = nodeOptions.alpha;
686
- if (this.highlightedNeighbors && this.highlightedNode) {
687
- if (!this.highlightedNeighbors.has(node.id) && this.highlightedNode.id != node.id) {
688
- alpha = this.highlighFading;
689
- }
690
- }
691
- this.context.beginPath();
692
- this.context.globalAlpha = alpha;
693
- /** text */
694
- if (nodeOptions.textVisible && nodeOptions.text) {
695
- textRenders.push(() => {
696
- if (!this.context || !node.x || !node.y || !nodeOptions.text)
697
- return;
698
- this.context.beginPath();
699
- this.context.globalAlpha = alpha;
700
- drawText({
701
- id: node.id,
702
- cachedNodeText: this.cachedNodeText,
703
- context: this.context,
704
- text: nodeOptions.text,
705
- textAlign: nodeOptions.textAlign,
706
- textColor: nodeOptions.textColor,
707
- textFont: nodeOptions.textFont,
708
- textSize: nodeOptions.textSize,
709
- x: node.x + nodeOptions.textShiftX,
710
- y: node.y + radius + nodeOptions.textShiftY,
711
- maxWidth: nodeOptions.textWidth,
712
- textStyle: nodeOptions.textStyle,
713
- textWeight: nodeOptions.textWeight,
714
- textGap: nodeOptions.textGap,
715
- });
716
- });
717
- }
718
- /** circle */
719
- this.context.lineWidth = nodeOptions.borderWidth;
720
- this.context.strokeStyle = nodeOptions.borderColor;
721
- this.context.fillStyle = nodeOptions.color;
722
- this.context.arc(node.x, node.y, radius, 0, 2 * Math.PI);
723
- this.context.fill();
724
- this.context.stroke();
725
- };
726
- return draw;
727
- }
728
- initResize() {
729
- if (!this.area)
730
- throw new Error("bad init data");
731
- let initialResizeCall = true;
732
- const abortController = this.eventAbortController;
733
- const observer = new ResizeObserver(() => {
734
- if (initialResizeCall) {
735
- initialResizeCall = false;
736
- return;
737
- }
738
- if (abortController.signal.aborted) {
739
- observer.disconnect();
740
- return;
741
- }
742
- requestAnimationFrame(() => {
743
- this.updateSize();
744
- });
745
- });
746
- observer.observe(this.area);
747
- }
748
- initPointer() {
749
- if (!this.area || !this.nodes || !this.simulation)
750
- throw new Error("bad init data");
751
- /** hover */
752
- this.area.addEventListener("pointermove", (event) => {
753
- let currentNode;
754
- if (this.graphSettings.highlightByHover && !this.isDragging) {
755
- currentNode = nodeByPointerGetter({
756
- graphSettings: this.graphSettings,
757
- areaRect: this.areaRect,
758
- areaTransform: this.areaTransform,
759
- mouseEvent: event,
760
- nodes: this.nodes,
761
- });
762
- if (currentNode && currentNode.neighbors && this.highlightedNode !== currentNode) {
763
- this.highlightedNode = currentNode;
764
- this.highlightedNeighbors = new Set(this.highlightedNode.neighbors);
765
- this.highlightFadingWorking = true;
766
- if (!this.simulationWorking && !this.highlightDrawing)
767
- requestAnimationFrame(() => {
768
- this.draw();
769
- });
770
- }
771
- else if (!currentNode && this.highlightedNode) {
772
- this.highlightFadingWorking = false;
773
- if (!this.simulationWorking && !this.highlightDrawing)
774
- requestAnimationFrame(() => {
775
- this.draw();
776
- });
777
- }
778
- }
779
- if (!this.listeners.onMove)
780
- return;
781
- if (!currentNode)
782
- currentNode = nodeByPointerGetter({
783
- graphSettings: this.graphSettings,
784
- areaRect: this.areaRect,
785
- areaTransform: this.areaTransform,
786
- mouseEvent: event,
787
- nodes: this.nodes,
788
- });
789
- return void this.listeners.onMove(event, currentNode);
790
- }, {
791
- signal: this.eventAbortController.signal,
792
- });
793
- /** dblclick */
794
- this.area.addEventListener("dblclick", (event) => {
795
- if (!this.listeners.onDoubleClick)
796
- return;
797
- const currentNode = nodeByPointerGetter({
798
- graphSettings: this.graphSettings,
799
- areaRect: this.areaRect,
800
- areaTransform: this.areaTransform,
801
- mouseEvent: event,
802
- nodes: this.nodes,
803
- });
804
- return void this.listeners.onDoubleClick(event, currentNode);
805
- });
806
- /** wheel click */
807
- this.area.addEventListener("mousedown", (event) => {
808
- if (this.isDragging || !this.listeners.onWheelClick || event.button !== 1)
809
- return;
810
- const currentNode = nodeByPointerGetter({
811
- graphSettings: this.graphSettings,
812
- areaRect: this.areaRect,
813
- areaTransform: this.areaTransform,
814
- mouseEvent: event,
815
- nodes: this.nodes,
816
- });
817
- return void this.listeners.onWheelClick(event, currentNode);
818
- }, {
819
- signal: this.eventAbortController.signal,
820
- });
821
- /** click */
822
- this.area.addEventListener("click", (event) => {
823
- if (this.isDragging || !this.listeners.onClick || event.button !== 0)
824
- return;
825
- const currentNode = nodeByPointerGetter({
826
- graphSettings: this.graphSettings,
827
- areaRect: this.areaRect,
828
- areaTransform: this.areaTransform,
829
- mouseEvent: event,
830
- nodes: this.nodes,
831
- });
832
- return void this.listeners.onClick(event, currentNode);
833
- }, {
834
- signal: this.eventAbortController.signal,
835
- });
836
- /** right click */
837
- this.area.addEventListener("contextmenu", (event) => {
838
- if (!this.listeners.onContextMenu)
839
- return;
840
- const currentNode = nodeByPointerGetter({
841
- graphSettings: this.graphSettings,
842
- areaRect: this.areaRect,
843
- areaTransform: this.areaTransform,
844
- mouseEvent: event,
845
- nodes: this.nodes,
846
- });
847
- return void this.listeners.onContextMenu(event, currentNode);
848
- }, {
849
- signal: this.eventAbortController.signal,
850
- });
851
- }
852
- initDnd() {
853
- if (!this.area || !this.nodes || !this.simulation)
854
- throw new Error("bad init data");
855
- select(this.area).call(drag()
856
- .subject((event) => {
857
- if (this.listeners.onDragSubject) {
858
- return this.listeners.onDragSubject(event, this.areaTransform, this.nodes);
859
- }
860
- if (!this.areaRect)
861
- return;
862
- const mouseEvent = event.sourceEvent;
863
- const [pointerX, pointerY] = pointerGetter(mouseEvent, this.areaRect, this.areaTransform);
864
- let index = 0;
865
- return greatest(this.nodes, (node) => {
866
- if (!node.x || !node.y)
867
- return undefined;
868
- const nodeOptions = nodeIterationExtractor(node, index, this.nodes, this.areaTransform, this.nodeSettings.options || {}, nodeOptionsGetter);
869
- const radius = nodeRadiusGetter({
870
- radiusFlexible: this.graphSettings.nodeRadiusFlexible,
871
- radiusInitial: this.graphSettings.nodeRadiusInitial,
872
- radiusCoefficient: this.graphSettings.nodeRadiusCoefficient,
873
- radiusFactor: this.graphSettings.nodeRadiusFactor,
874
- linkCount: node.linkCount,
875
- }) ?? nodeOptions.radius;
876
- index++;
877
- return this.graphSettings.dragPlaceCoefficient(node, pointerX, pointerY, radius);
878
- });
879
- })
880
- .on("start", (event) => {
881
- this.listeners.onStartDragFinished?.(event, this.simulation, this.areaTransform);
882
- })
883
- .on("drag", (event) => {
884
- if (!this.isDragging) {
885
- this.isDragging = true;
886
- if (this.simulation)
887
- this.simulation.alphaTarget(0.3).restart();
888
- }
889
- if (!this.areaRect)
890
- return;
891
- const mouseEvent = event.sourceEvent;
892
- const [pointerX, pointerY] = pointerGetter(mouseEvent, this.areaRect, this.areaTransform);
893
- event.subject.fx = pointerX;
894
- event.subject.fy = pointerY;
895
- this.listeners.onMoveDragFinished?.(event, this.simulation, this.areaTransform);
896
- })
897
- .on("end", (event) => {
898
- this.isDragging = false;
899
- if (!event.active && this.simulation)
900
- this.simulation.alphaTarget(0);
901
- if (this.graphSettings.stickAfterDrag && this.areaRect) {
902
- if (!this.areaRect)
903
- return;
904
- const mouseEvent = event.sourceEvent;
905
- const [pointerX, pointerY] = pointerGetter(mouseEvent, this.areaRect, this.areaTransform);
906
- event.subject.fx = pointerX;
907
- event.subject.fy = pointerY;
908
- }
909
- else {
910
- event.subject.fx = null;
911
- event.subject.fy = null;
912
- }
913
- this.listeners.onEndDragFinished?.(event, this.simulation, this.areaTransform);
914
- }));
915
- }
916
- initZoom() {
917
- if (!this.area)
918
- throw new Error("bad init data");
919
- select(this.area)
920
- .call(zoom()
921
- .scaleExtent(this.graphSettings.zoomExtent)
922
- .on("zoom", (event) => {
923
- this.listeners.onZoom?.(event);
924
- this.areaTransform = event.transform;
925
- if (!this.simulationWorking)
926
- requestAnimationFrame(() => this.draw());
927
- }))
928
- .on("dblclick.zoom", null);
929
- const zoomInitial = this.graphSettings.zoomInitial;
930
- this.areaTransform = new ZoomTransform(zoomInitial?.k ?? 1, zoomInitial?.x ?? this.width / 2, zoomInitial?.y ?? this.height / 2);
931
- zoom().transform(select(this.area), this.areaTransform);
932
- }
933
- }
934
-
935
- export { GraphCanvas };
1
+ export { GraphCanvas } from './module/GraphCanvas/GraphCanvas.js';
2
+ export { linkOptionsGetter } from './module/GraphCanvas/lib/settings/link-settings-getter.js';
3
+ export { nodeOptionsGetter, nodeRadiusGetter } from './module/GraphCanvas/lib/settings/node-settings-getter.js';
4
+ export { linkIterationExtractor } from './module/GraphCanvas/lib/utils/link-iteration-extractor.js';
5
+ import 'd3-array';
6
+ export { nodeIterationExtractor } from './module/GraphCanvas/lib/utils/node-iteration-extractor.js';
7
+ export { drawText } from './module/GraphCanvas/lib/utils/draw-text.js';
8
+ export { calculateLinkPositionByRadius } from './module/GraphCanvas/lib/utils/calculate-link-position-by-radius.js';
9
+ export { colorToRgb } from './lib/color-to-rgb.js';
10
+ export { extractRgb } from './lib/extract-rgb.js';
11
+ export { fadeRgb } from './lib/fade-rgb.js';
12
+ export { rgbAnimationByProgress } from './lib/rgb-animation-by-progress.js';
936
13
  //# sourceMappingURL=index.js.map