Skip to content

Commit 6dfe38f

Browse files
committed
refactor: simplify graph readiness and zoom handling; replace unstable checks with waitUsableRectUpdate method
1 parent 4358b23 commit 6dfe38f

File tree

8 files changed

+33
-84
lines changed

8 files changed

+33
-84
lines changed

src/api/PublicGraphApi.ts

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,27 +35,9 @@ export class PublicGraphApi {
3535
* @param zoomConfig - Configuration for zoom transition and padding
3636
* @returns Promise that resolves when zoom operation is complete
3737
*/
38-
public zoomToViewPort(zoomConfig?: ZoomConfig): Promise<void> {
39-
return new Promise((resolve) => {
40-
const currentRect = this.getUsableRect();
41-
42-
if (this.graph.hitTest.isUnstable) {
43-
const unsubscribe = this.graph.hitTest.onUsableRectUpdate((usableRect) => {
44-
if (this.graph.hitTest.isUnstable) {
45-
return;
46-
}
47-
48-
this.zoomToRect(usableRect, zoomConfig);
49-
resolve();
50-
setTimeout(() => {
51-
unsubscribe();
52-
}, 0);
53-
});
54-
return;
55-
}
56-
57-
this.zoomToRect(currentRect, zoomConfig);
58-
resolve();
38+
public zoomToViewPort(zoomConfig?: ZoomConfig) {
39+
this.graph.hitTest.waitUsableRectUpdate((rect) => {
40+
this.zoomToRect(rect, zoomConfig);
5941
});
6042
}
6143

src/components/canvas/connections/BatchPath2D/index.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { ESchedulerPriority } from "../../../../lib";
21
import { cache } from "../../../../lib/utils";
3-
import { debounce } from "../../../../utils/functions";
42

53
export type Path2DRenderStyleResult =
64
| { type: "stroke" }

src/components/canvas/layers/graphLayer/GraphLayer.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { Graph } from "../../../../graph";
22
import { GraphMouseEventNames, isNativeGraphEventName } from "../../../../graphEvents";
3-
import { ESchedulerPriority } from "../../../../lib";
43
import { Component } from "../../../../lib/Component";
54
import { Layer, LayerContext, LayerProps } from "../../../../services/Layer";
65
import { Camera, TCameraProps } from "../../../../services/camera/Camera";
76
import { ICamera } from "../../../../services/camera/CameraService";
8-
import { getEventDelta, schedule } from "../../../../utils/functions";
7+
import { getEventDelta } from "../../../../utils/functions";
98
import { EventedComponent } from "../../EventedComponent/EventedComponent";
109
import { Blocks } from "../../blocks/Blocks";
1110
import { BlockConnection } from "../../connections/BlockConnection";

src/graph.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -366,17 +366,7 @@ export class Graph {
366366
* @param cb - Callback to run after graph is ready
367367
*/
368368
public runAfterGraphReady(cb: () => void) {
369-
if (this.hitTest.isUnstable) {
370-
this.hitTest.on("update", () => {
371-
if (this.hitTest.isUnstable) {
372-
this.runAfterGraphReady(cb);
373-
return;
374-
}
375-
cb();
376-
});
377-
} else {
378-
cb();
379-
}
369+
this.hitTest.waitUsableRectUpdate(cb);
380370
}
381371

382372
public stop(full = false) {

src/react-components/BlocksList.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,12 @@ export const BlocksList = memo(function BlocksList({ renderBlock, graphObject }:
125125
throttleUpdate.cancel();
126126
scheduleListUpdate.cancel();
127127
};
128-
}, [graphObject.cameraService]);
128+
}, []);
129129

130130
// init list
131-
useLayoutEffect(() => {
132-
graphObject.hitTest.on("update", throttleUpdate);
133-
134-
throttleUpdate(graphObject.cameraService.getCameraState());
135-
return () => {
136-
graphObject.hitTest.off("update", updateBlockList);
137-
};
138-
}, [graphObject.cameraService, graphObject.hitTest, updateBlockList]);
131+
useEffect(() => {
132+
return graphObject.hitTest.onUsableRectUpdate(updateBlockList);
133+
}, [graphObject.hitTest, throttleUpdate, isRenderAllowed, graphState]);
139134

140135
return (
141136
<>

src/services/HitTest.ts

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,14 @@ export interface IHitBox extends HitBoxData {
3333
export class HitTest extends Emitter {
3434
private tree = new RBush<HitBox>(9);
3535

36-
protected empty = true;
37-
3836
protected $usableRect = signal<TRect>({ x: 0, y: 0, width: 0, height: 0 });
3937

4038
// Single queue replaces all complex state tracking
4139
protected queue = new Map<HitBox, HitBoxData | null>();
4240

4341
public get isUnstable() {
4442
return (
43+
this.processQueue.isScheduled() ||
4544
this.queue.size > 0 ||
4645
(this.$usableRect.value.height === 0 &&
4746
this.$usableRect.value.width === 0 &&
@@ -57,8 +56,7 @@ export class HitTest extends Emitter {
5756
// Single debounced job replaces complex scheduler logic
5857
protected processQueue = debounce(
5958
() => {
60-
let needUpdateUsableRect = false;
61-
59+
const items = [];
6260
for (const [item, bbox] of this.queue) {
6361
if (bbox === null) {
6462
// Remove operation
@@ -67,29 +65,28 @@ export class HitTest extends Emitter {
6765
// Add/update operation
6866
this.tree.remove(item);
6967
item.updateRect(bbox);
70-
this.tree.load([item]);
71-
needUpdateUsableRect = true;
68+
items.push(item);
7269
}
7370
}
71+
this.tree.load(items);
7472
this.queue.clear();
75-
76-
if (needUpdateUsableRect) {
77-
this.updateUsableRect();
78-
}
73+
this.updateUsableRect();
7974
this.emit("update", this);
8075
},
8176
{
82-
priority: ESchedulerPriority.HIGHEST,
77+
priority: ESchedulerPriority.LOWEST,
8378
frameTimeout: 250,
8479
}
8580
);
8681

87-
public update(item: HitBox, bbox: HitBoxData, force = false) {
82+
public update(item: HitBox, bbox: HitBoxData, _force = false) {
8883
this.queue.set(item, bbox);
8984
this.processQueue();
90-
if (force) {
85+
// TODO: force update may be cause to unset unstable flag before graph is really made stable
86+
// Usually case: updateEntities update blocks and connections. In this case used force stategy, so every entity will be updated immediatelly, but async, so zoom will be unstable
87+
/* if (force) {
9188
this.processQueue.flush();
92-
}
89+
} */
9390
}
9491

9592
public clear() {
@@ -109,25 +106,13 @@ export class HitTest extends Emitter {
109106
this.processQueue();
110107
}
111108

112-
protected getBBox(items: HitBox[]) {
113-
return items.reduce(
114-
(acc, item) => {
115-
if (Number.isFinite(item.minX)) {
116-
acc.minX = Math.min(acc.minX, item.minX);
117-
}
118-
if (Number.isFinite(item.minY)) {
119-
acc.minY = Math.min(acc.minY, item.minY);
120-
}
121-
if (Number.isFinite(item.maxX)) {
122-
acc.maxX = Math.max(acc.maxX, item.maxX);
123-
}
124-
if (Number.isFinite(item.maxY)) {
125-
acc.maxY = Math.max(acc.maxY, item.maxY);
126-
}
127-
return acc;
128-
},
129-
{ minX: 0, minY: 0, maxX: 0, maxY: 0 }
130-
);
109+
public waitUsableRectUpdate(callback: (rect: TRect) => void) {
110+
if (this.isUnstable) {
111+
return this.once("update", () => {
112+
this.waitUsableRectUpdate(callback);
113+
});
114+
}
115+
callback(this.$usableRect.value);
131116
}
132117

133118
protected updateUsableRect() {

src/stories/Playground/GraphPlayground.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { TBlock } from "../../components/canvas/blocks/Block";
77
import { random } from "../../components/canvas/blocks/generate";
88
import { ConnectionLayer } from "../../components/canvas/layers/connectionLayer/ConnectionLayer";
99
import { Graph, GraphState, TGraphConfig } from "../../graph";
10-
import { ESchedulerPriority } from "../../lib";
1110
import { GraphBlock, GraphCanvas, HookGraphParams, useGraph, useGraphEvent, useLayer } from "../../react-components";
1211
import { useFn } from "../../react-components/utils/hooks/useFn";
1312
import { ECanChangeBlockGeometry } from "../../store/settings";
@@ -31,8 +30,6 @@ import {
3130
import "./Playground.css";
3231
import "@gravity-ui/uikit/styles/styles.css";
3332

34-
import { schedule } from "../../utils/functions";
35-
3633
const generated = generatePlaygroundActionBlocks(0, 5);
3734
const textBlocks = [
3835
createTextBlock(-144, 80, 448, 0, "To create new block, drag and drop new connection from edge"),

src/utils/utils/schedule.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const debounce = <T extends (...args: unknown[]) => void>(
5252
frameInterval?: number;
5353
frameTimeout?: number;
5454
} = {}
55-
): T & { cancel: () => void; flush: () => void } => {
55+
): T & { cancel: () => void; flush: () => void; isScheduled: () => boolean } => {
5656
let frameCounter = 0;
5757
let isScheduled = false;
5858
let removeScheduler: (() => void) | null = null;
@@ -66,10 +66,10 @@ export const debounce = <T extends (...args: unknown[]) => void>(
6666
const elapsedTime = currentTime - startTime;
6767

6868
if (frameCounter >= frameInterval && elapsedTime >= frameTimeout) {
69-
fn(...latestArgs);
7069
isScheduled = false;
7170
frameCounter = 0;
7271
startTime = 0;
72+
fn(...latestArgs);
7373
if (removeScheduler) {
7474
removeScheduler();
7575
removeScheduler = null;
@@ -104,10 +104,13 @@ export const debounce = <T extends (...args: unknown[]) => void>(
104104
isScheduled = true;
105105
removeScheduler = scheduler.addScheduler(debouncedScheduler, priority);
106106
}
107-
}) as T & { cancel: () => void; flush: () => void };
107+
}) as T & { cancel: () => void; flush: () => void; isScheduled: () => boolean };
108108

109109
debouncedFn.cancel = cancel;
110110
debouncedFn.flush = flush;
111+
debouncedFn.isScheduled = () => {
112+
return isScheduled;
113+
};
111114

112115
return debouncedFn;
113116
};

0 commit comments

Comments
 (0)