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