2025-02-10 16:40:19 +01:00
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
import { baseUrl } from "./api";
|
|
|
|
|
2025-02-12 16:53:06 +01:00
|
|
|
//const debounce = <T extends (...args: any[]) => void>(
|
|
|
|
// func: T,
|
|
|
|
// delay: number
|
|
|
|
//): ((...args: Parameters<T>) => void) => {
|
|
|
|
// let timeoutId: number | null = null;
|
|
|
|
// return (...args: Parameters<T>) => {
|
|
|
|
// if (timeoutId !== null) {
|
|
|
|
// clearTimeout(timeoutId);
|
|
|
|
// }
|
|
|
|
// console.log(timeoutId);
|
|
|
|
// timeoutId = setTimeout(() => {
|
|
|
|
// func(...args);
|
|
|
|
// }, delay);
|
|
|
|
// };
|
|
|
|
//};
|
|
|
|
//
|
2025-02-11 14:14:01 +01:00
|
|
|
interface Prop {
|
|
|
|
name: string;
|
|
|
|
min: string;
|
|
|
|
max: string;
|
|
|
|
step: string;
|
|
|
|
value: string;
|
|
|
|
}
|
|
|
|
|
2025-02-10 16:40:19 +01:00
|
|
|
interface Params {
|
|
|
|
nodeSize: number;
|
|
|
|
edgeWidth: number;
|
|
|
|
arrowSize: number;
|
|
|
|
fontSize: number;
|
2025-02-11 14:14:01 +01:00
|
|
|
distance: number;
|
2025-02-12 15:39:52 +01:00
|
|
|
weighting: boolean;
|
|
|
|
popularity: boolean;
|
2025-02-12 17:23:18 +01:00
|
|
|
dislike: boolean;
|
2025-02-10 16:40:19 +01:00
|
|
|
}
|
|
|
|
|
2025-02-12 16:53:06 +01:00
|
|
|
interface DeferredProps {
|
|
|
|
timeout: number;
|
|
|
|
func: () => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let timeoutID: number | null = null;
|
2025-02-10 16:40:19 +01:00
|
|
|
export default function Analysis() {
|
|
|
|
const [image, setImage] = useState("");
|
|
|
|
const [params, setParams] = useState<Params>({
|
2025-02-11 14:14:01 +01:00
|
|
|
nodeSize: 2000,
|
2025-02-10 16:40:19 +01:00
|
|
|
edgeWidth: 1,
|
2025-02-12 17:23:18 +01:00
|
|
|
arrowSize: 16,
|
2025-02-10 16:40:19 +01:00
|
|
|
fontSize: 10,
|
2025-02-11 16:30:53 +01:00
|
|
|
distance: 2,
|
2025-02-12 15:39:52 +01:00
|
|
|
weighting: true,
|
|
|
|
popularity: true,
|
2025-02-12 17:23:18 +01:00
|
|
|
dislike: false,
|
2025-02-10 16:40:19 +01:00
|
|
|
});
|
|
|
|
const [showControlPanel, setShowControlPanel] = useState(false);
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
|
|
|
// Function to generate and fetch the graph image
|
|
|
|
async function loadImage() {
|
|
|
|
setLoading(true);
|
2025-02-11 14:14:01 +01:00
|
|
|
await fetch(`${baseUrl}api/analysis/image`, {
|
2025-02-10 16:40:19 +01:00
|
|
|
method: "POST",
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
},
|
|
|
|
body: JSON.stringify(params)
|
|
|
|
})
|
|
|
|
.then((resp) => resp.json())
|
|
|
|
.then((data) => {
|
|
|
|
setImage(data.image);
|
|
|
|
setLoading(false);
|
|
|
|
});
|
|
|
|
}
|
2025-02-12 17:23:18 +01:00
|
|
|
|
2025-02-10 16:40:19 +01:00
|
|
|
useEffect(() => {
|
2025-02-12 16:53:06 +01:00
|
|
|
if (timeoutID) {
|
|
|
|
clearTimeout(timeoutID);
|
|
|
|
}
|
|
|
|
timeoutID = setTimeout(() => {
|
|
|
|
loadImage();
|
2025-02-12 17:23:18 +01:00
|
|
|
}, 1000);
|
2025-02-12 16:53:06 +01:00
|
|
|
}, [params]);
|
2025-02-10 16:40:19 +01:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="stack column dropdown">
|
|
|
|
<button onClick={() => setShowControlPanel(!showControlPanel)}>
|
2025-02-11 20:55:15 +01:00
|
|
|
Parameters <svg viewBox="0 0 24 24" height="1.2em" style={{ fill: "#ffffff", display: "inline", top: "0.2em", position: "relative", transform: showControlPanel ? "rotate(180deg)" : "unset" }} > <path d="M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z" > </path></svg >
|
2025-02-10 16:40:19 +01:00
|
|
|
</button>
|
2025-02-11 16:30:53 +01:00
|
|
|
<div id="control-panel" className={showControlPanel ? "opened" : ""}>
|
2025-02-11 14:14:01 +01:00
|
|
|
|
2025-02-12 15:39:52 +01:00
|
|
|
<div className="control">
|
2025-02-12 17:23:18 +01:00
|
|
|
<div className="checkBox">
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={params.dislike}
|
|
|
|
onChange={(evt) => setParams({ ...params, dislike: evt.target.checked })}
|
|
|
|
/>
|
|
|
|
<label>show dislike</label>
|
|
|
|
</div>
|
2025-02-12 15:39:52 +01:00
|
|
|
<div className="checkBox">
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={params.weighting}
|
|
|
|
onChange={(evt) => setParams({ ...params, weighting: evt.target.checked })}
|
2025-02-12 16:53:06 +01:00
|
|
|
/>
|
|
|
|
<label>weighting</label>
|
|
|
|
</div>
|
2025-02-12 15:39:52 +01:00
|
|
|
|
|
|
|
<div className="checkBox">
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
checked={params.popularity}
|
2025-02-12 16:53:06 +01:00
|
|
|
onChange={(evt) => setParams({ ...params, popularity: evt.target.checked })}
|
2025-02-12 15:39:52 +01:00
|
|
|
/>
|
2025-02-12 16:53:06 +01:00
|
|
|
<label>popularity</label>
|
2025-02-12 15:39:52 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2025-02-11 14:14:01 +01:00
|
|
|
<div className="control">
|
|
|
|
<label>distance between nodes</label>
|
|
|
|
<input
|
|
|
|
type="range"
|
|
|
|
min="0.01"
|
|
|
|
max="3.001"
|
|
|
|
step="0.05"
|
|
|
|
value={params.distance}
|
|
|
|
onChange={(evt) => setParams({ ...params, distance: Number(evt.target.value) })}
|
|
|
|
/>
|
|
|
|
<span>{params.distance}</span></div>
|
2025-02-10 16:40:19 +01:00
|
|
|
|
2025-02-11 14:14:01 +01:00
|
|
|
<div className="control">
|
|
|
|
<label>node size</label>
|
2025-02-10 16:40:19 +01:00
|
|
|
<input
|
|
|
|
type="range"
|
|
|
|
min="500"
|
|
|
|
max="3000"
|
|
|
|
value={params.nodeSize}
|
|
|
|
onChange={(evt) => setParams({ ...params, nodeSize: Number(evt.target.value) })}
|
|
|
|
/>
|
|
|
|
<span>{params.nodeSize}</span>
|
2025-02-11 14:14:01 +01:00
|
|
|
</div>
|
2025-02-10 16:40:19 +01:00
|
|
|
|
2025-02-11 14:14:01 +01:00
|
|
|
<div className="control">
|
|
|
|
<label>font size</label>
|
2025-02-10 16:40:19 +01:00
|
|
|
<input
|
|
|
|
type="range"
|
|
|
|
min="4"
|
|
|
|
max="24"
|
|
|
|
value={params.fontSize}
|
|
|
|
onChange={(evt) => setParams({ ...params, fontSize: Number(evt.target.value) })}
|
|
|
|
/>
|
|
|
|
<span>{params.fontSize}</span>
|
2025-02-11 14:14:01 +01:00
|
|
|
</div>
|
2025-02-10 16:40:19 +01:00
|
|
|
|
2025-02-11 14:14:01 +01:00
|
|
|
<div className="control">
|
|
|
|
<label>edge width</label>
|
2025-02-10 16:40:19 +01:00
|
|
|
<input
|
|
|
|
type="range"
|
|
|
|
min="1"
|
|
|
|
max="5"
|
|
|
|
step="0.1"
|
|
|
|
value={params.edgeWidth}
|
|
|
|
onChange={(evt) => setParams({ ...params, edgeWidth: Number(evt.target.value) })}
|
|
|
|
/>
|
|
|
|
<span>{params.edgeWidth}</span>
|
2025-02-11 14:14:01 +01:00
|
|
|
</div>
|
2025-02-10 16:40:19 +01:00
|
|
|
|
2025-02-11 14:14:01 +01:00
|
|
|
<div className="control">
|
|
|
|
<label>arrow size</label>
|
2025-02-10 16:40:19 +01:00
|
|
|
<input
|
|
|
|
type="range"
|
|
|
|
min="10"
|
|
|
|
max="50"
|
|
|
|
value={params.arrowSize}
|
|
|
|
onChange={(evt) => setParams({ ...params, arrowSize: Number(evt.target.value) })}
|
|
|
|
/>
|
|
|
|
<span>{params.arrowSize}</span>
|
|
|
|
</div>
|
2025-02-11 14:14:01 +01:00
|
|
|
|
|
|
|
</div>
|
|
|
|
<button onClick={() => loadImage()}>reload ↻</button>
|
2025-02-11 20:55:15 +01:00
|
|
|
{
|
|
|
|
loading ? (
|
|
|
|
<span className="loader"></span>
|
|
|
|
) : (
|
|
|
|
<img src={"data:image/png;base64," + image} width="86%" />
|
|
|
|
)
|
|
|
|
}
|
|
|
|
</div >
|
2025-02-10 16:40:19 +01:00
|
|
|
);
|
|
|
|
}
|