Skip to content

Commit 9a5ce85

Browse files
authored
fix: Refresh updates
1 parent 6778bb2 commit 9a5ce85

File tree

3 files changed

+120
-11
lines changed

3 files changed

+120
-11
lines changed

packages/treeviz/demo/demo.html

+26-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
<input type="button" id="add" value="Add data" style="font-size:35px;"></input>
1111
<input type="button" id="remove" value="Remove data"style="font-size:35px;"></input>
12+
<input type="button" id="doTasks" value="Edit data multiple times" style="font-size:35px;"></input>
1213

1314
<div id="tree" style="height:800px;width:1000px"></div>
1415

@@ -67,9 +68,31 @@
6768
onNodeClick : (nodeData) => console.log(nodeData)
6869
});
6970
myTree.refresh(data_1);
70-
var toggle=true;
71-
document.querySelector("#add").addEventListener("click", () => {toggle ? myTree.refresh(data_2) : myTree.refresh(data_3); toggle = false});
72-
document.querySelector("#remove").addEventListener("click", () => myTree.refresh(data_1));
71+
let toggle=true;
72+
let addButton = document.querySelector("#add");
73+
let removeButton = document.querySelector("#remove");
74+
let doTasksButton = document.querySelector("#doTasks")
75+
addButton.addEventListener("click", () => {
76+
console.log('addButton clicked');
77+
toggle ? myTree.refresh(data_2) : myTree.refresh(data_3); toggle = false;
78+
});
79+
removeButton.addEventListener("click", () => {
80+
console.log('removeButton clicked');
81+
myTree.refresh(data_1);
82+
});
83+
doTasksButton.addEventListener("click", () => {
84+
addButton.click();
85+
removeButton.click();
86+
addButton.click();
87+
removeButton.click();
88+
removeButton.click();
89+
addButton.click();
90+
removeButton.click();
91+
addButton.click();
92+
addButton.click();
93+
removeButton.click();
94+
removeButton.click();
95+
});
7396

7497
</script>
7598
<style>

packages/treeviz/src/index.ts

+11-8
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { drawNodeExit } from "./nodes/node-exit";
99
import { drawNodeUpdate } from "./nodes/node-update";
1010
import { generateBasicTreemap, generateNestedData } from "./prepare-data";
1111
import { ExtendedHierarchyPointNode, ITreeConfig } from "./typings";
12+
import { RefreshQueue } from "./utils";
1213

1314
export function create(userSettings: Partial<ITreeConfig>) {
1415
const defaultSettings: ITreeConfig = {
@@ -97,15 +98,17 @@ export function create(userSettings: Partial<ITreeConfig>) {
9798
}
9899

99100
function refresh(data: any, newSettings?: Partial<ITreeConfig>) {
100-
if (newSettings) {
101-
settings = { ...settings, ...newSettings };
102-
}
103-
const nestedData = generateNestedData(data, settings);
104-
const treemap = generateBasicTreemap(settings);
105-
const computedTree = treemap(nestedData); // mutation
101+
RefreshQueue.add(settings.duration, ()=> {
102+
if (newSettings) {
103+
settings = { ...settings, ...newSettings };
104+
}
105+
const nestedData = generateNestedData(data, settings);
106+
const treemap = generateBasicTreemap(settings);
107+
const computedTree = treemap(nestedData); // mutation
106108

107-
// @ts-ignore
108-
draw(svg, computedTree);
109+
// @ts-ignore
110+
draw(svg, computedTree);
111+
});
109112
}
110113

111114
function clean(keepConfig: boolean) {

packages/treeviz/src/utils.ts

+83
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,86 @@ export const setNodeLocation = (
5454
return "translate(" + xPosition + "," + yPosition + ")";
5555
}
5656
};
57+
58+
// RefreshQueue ensures that don't run a refresh while another refresh
59+
// is in transition.
60+
export class RefreshQueue {
61+
// The queue is an array that contains objects. Each object represents an
62+
// refresh action and only they have 2 properties:
63+
// {
64+
// callback: triggers when it's the first of queue and then it
65+
// becomes null to prevent that callback executes more
66+
// than once.
67+
// delayNextCallback: when callback is executed, queue will subtracts
68+
// milliseconds from it. When it becomes 0, the entire
69+
// object is destroyed (shifted) from the array and then
70+
// the next item (if exists) will be executed similary
71+
// to this.
72+
// }
73+
private static queue: Array<{ delayNextCallback: number, callback: any }> = [];
74+
75+
// Contains setInterval ID
76+
private static runner: number;
77+
78+
// Milliseconds of each iteration
79+
private static runnerSpeed: number = 100;
80+
81+
// Developer internal magic number. Time added at end of refresh transition to
82+
// let DOM and d3 rest before another refresh.
83+
// 0 creates console and visual errors because getFirstDisplayedAncestor never
84+
// found the needed id and setNodeLocation receives undefined parameters.
85+
// Between 50 and 100 milliseconds seems enough for 10 nodes (demo example)
86+
private static readonly extraDelayBetweenCallbacks: number = 100;
87+
88+
// Developer internal for debugging RefreshQueue class. Set true to see
89+
// console "real time" queue of tasks.
90+
// If there is a cleaner method, remove it!
91+
private static showQueueLog: boolean = false;
92+
93+
// Adds one refresh action to the queue. When safe callback will be
94+
// triggered
95+
public static add(duration: number, callback: () => any) {
96+
this.queue.push(
97+
{
98+
delayNextCallback: duration + this.extraDelayBetweenCallbacks,
99+
callback: callback
100+
});
101+
this.log(this.queue.map(_ => _.delayNextCallback), "<-- New task !!!");
102+
if (!this.runner) {
103+
this.runnerFunction();
104+
//@ts-ignore
105+
this.runner = setInterval(() => this.runnerFunction(), this.runnerSpeed);
106+
}
107+
}
108+
109+
// Each this.runnerSpeed milliseconds it's executed. It stops when finish.
110+
private static runnerFunction() {
111+
if (this.queue[0]) {
112+
// ************************ Callback section ************************
113+
if (this.queue[0].callback) {
114+
this.log("Executing task, delaying next task...");
115+
try {
116+
this.queue[0].callback();
117+
} catch (e) {
118+
console.error(e);
119+
} finally {
120+
// To prevent trigger callback more than once
121+
this.queue[0].callback = null;
122+
}
123+
}
124+
// ******************** Delay until next callback ********************
125+
this.queue[0].delayNextCallback -= this.runnerSpeed;
126+
this.log(this.queue.map(_ => _.delayNextCallback));
127+
if (this.queue[0].delayNextCallback <= 0) {
128+
this.queue.shift();
129+
}
130+
} else {
131+
this.log("No task found");
132+
clearInterval(this.runner);
133+
this.runner = 0;
134+
}
135+
};
136+
137+
// Print to console debug data if this.showQueueLog = true
138+
private static log(...msg: any) {if (this.showQueueLog) console.log(...msg)}
139+
}

0 commit comments

Comments
 (0)