d3 playground
This commit is contained in:
commit
920e7b98d0
37
ideaweb/graph.mjs
Normal file
37
ideaweb/graph.mjs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
export default class Graph {
|
||||||
|
constructor() {
|
||||||
|
this.Default = 0x01;
|
||||||
|
this.KindCategory = 0x02;
|
||||||
|
this.KindProject = 0x04;
|
||||||
|
this.InboxId = 1;
|
||||||
|
this.nodes = [
|
||||||
|
{ id: this.InboxId, name: "INBOX", kind: this.KindCategory },
|
||||||
|
{ id: 2000, name: "UChicago", kind: this.KindCategory },
|
||||||
|
{ id: 2100, name: "51042", kind: this.KindProject },
|
||||||
|
{ id: 2400, name: "30239", kind: this.KindProject },
|
||||||
|
{ id: 2410, name: "D3", kind: this.Default },
|
||||||
|
{ id: 2420, name: "Altair", kind: this.Default },
|
||||||
|
];
|
||||||
|
this.links = [
|
||||||
|
{ source: 2000, target: 2100 },
|
||||||
|
{ source: 2000, target: 2400 },
|
||||||
|
{ source: 2400, target: 2410 },
|
||||||
|
{ source: 2400, target: 2420 },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(name, kind = "default") {
|
||||||
|
// todo: fix id
|
||||||
|
let id = Math.floor(Math.random() * 20000000000);
|
||||||
|
this.nodes.push({
|
||||||
|
id: id,
|
||||||
|
name,
|
||||||
|
kind: "default",
|
||||||
|
});
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
addLink(source, target) {
|
||||||
|
this.links.push({ source, target });
|
||||||
|
}
|
||||||
|
}
|
116
ideaweb/index.html
Normal file
116
ideaweb/index.html
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div id="container"></div>
|
||||||
|
<script src="../js/d3.v7.js"></script>
|
||||||
|
<script type="module">
|
||||||
|
import Graph from "./graph.mjs";
|
||||||
|
import ModalEditor from "./modal.mjs";
|
||||||
|
const graph = new Graph();
|
||||||
|
const editor = new ModalEditor(graph);
|
||||||
|
|
||||||
|
const width = 800;
|
||||||
|
const height = 800;
|
||||||
|
const svg = d3
|
||||||
|
.create("svg")
|
||||||
|
.attr("width", width)
|
||||||
|
.attr("height", height)
|
||||||
|
.attr("viewBox", [-width / 2, -height / 2, width, height])
|
||||||
|
.attr("style", "max-width: 100%; height: auto;");
|
||||||
|
const color = d3.scaleOrdinal(d3.schemeCategory10);
|
||||||
|
const simulation = d3
|
||||||
|
.forceSimulation(graph.nodes)
|
||||||
|
.force(
|
||||||
|
"link",
|
||||||
|
d3.forceLink(graph.links).id((d) => d.id),
|
||||||
|
)
|
||||||
|
.force("charge", d3.forceManyBody())
|
||||||
|
.force("x", d3.forceX())
|
||||||
|
.force("y", d3.forceY());
|
||||||
|
const selectionGroup = svg.append("g");
|
||||||
|
const nodeGroup = svg.append("g");
|
||||||
|
const linkGroup = svg
|
||||||
|
.append("g")
|
||||||
|
.attr("stroke", "#999")
|
||||||
|
.attr("stroke-opacity", 0.6);
|
||||||
|
const selection = selectionGroup
|
||||||
|
.append("circle")
|
||||||
|
.style("fill", "#ccccccbb")
|
||||||
|
.style("stroke", "#718e51")
|
||||||
|
.style("stroke-width", 1)
|
||||||
|
.attr("r", 10);
|
||||||
|
let selectionId = 1;
|
||||||
|
|
||||||
|
function updateSelection() {
|
||||||
|
for (let n of graph.nodes) {
|
||||||
|
if (n.id == selectionId) {
|
||||||
|
selection.attr("cx", n.x).attr("cy", n.y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function graphLayout() {
|
||||||
|
const link = linkGroup
|
||||||
|
.selectAll("line")
|
||||||
|
.data(graph.links, (d) => `${d.source},${d.target}`)
|
||||||
|
.join("line");
|
||||||
|
|
||||||
|
const node = nodeGroup
|
||||||
|
.selectAll("g")
|
||||||
|
.data(graph.nodes, (d) => d.id)
|
||||||
|
.join(function (enter) {
|
||||||
|
const lg = enter.append("g");
|
||||||
|
lg.append("circle")
|
||||||
|
.attr("r", 5)
|
||||||
|
.attr("stroke-width", 1.5)
|
||||||
|
.attr("fill", (d) => color(d.kind));
|
||||||
|
lg.append("text")
|
||||||
|
.attr("dy", ".35em")
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.style("font", "10px monospace")
|
||||||
|
.text((d) => d.name);
|
||||||
|
return lg;
|
||||||
|
})
|
||||||
|
.attr("transform", (d) => `translate(${d.x}, ${d.y})`);
|
||||||
|
|
||||||
|
simulation.nodes(graph.nodes);
|
||||||
|
simulation.force("link").links(graph.links);
|
||||||
|
simulation.alpha(1).restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
graphLayout();
|
||||||
|
|
||||||
|
const commandBar = svg
|
||||||
|
.append("g")
|
||||||
|
.append("text")
|
||||||
|
.attr("transform", `translate(50, 50)`)
|
||||||
|
.attr("transform", `translate(${-width / 2 + 10}, ${height / 2})`)
|
||||||
|
.style("font", "10px monospace")
|
||||||
|
.text("command bar here")
|
||||||
|
.style("fill", "darkgreen");
|
||||||
|
|
||||||
|
// update the node positions on each tick
|
||||||
|
simulation.on("tick", () => {
|
||||||
|
linkGroup
|
||||||
|
.selectAll("line")
|
||||||
|
.attr("x1", (d) => d.source.x)
|
||||||
|
.attr("y1", (d) => d.source.y)
|
||||||
|
.attr("x2", (d) => d.target.x)
|
||||||
|
.attr("y2", (d) => d.target.y);
|
||||||
|
nodeGroup
|
||||||
|
.selectAll("g")
|
||||||
|
.attr("transform", (d) => `translate(${d.x}, ${d.y})`);
|
||||||
|
updateSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
d3.select("body").on("keyup", function (e) {
|
||||||
|
editor.handleKeyup(e);
|
||||||
|
commandBar.text(editor.display());
|
||||||
|
graphLayout();
|
||||||
|
});
|
||||||
|
|
||||||
|
container.append(svg.node());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
47
ideaweb/modal.mjs
Normal file
47
ideaweb/modal.mjs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
export default class ModalEditor {
|
||||||
|
constructor(graph) {
|
||||||
|
this.graph = graph;
|
||||||
|
this.mode = "command";
|
||||||
|
this.word = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyup(event) {
|
||||||
|
console.log(event);
|
||||||
|
switch (this.mode) {
|
||||||
|
case "command":
|
||||||
|
this.commandModeKey(event);
|
||||||
|
break;
|
||||||
|
case "insert":
|
||||||
|
this.insertModeKey(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commandModeKey(event) {
|
||||||
|
switch (event.key) {
|
||||||
|
case "i":
|
||||||
|
this.mode = "insert";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertModeKey(event) {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
let newId = this.graph.insert(this.word, "unknown");
|
||||||
|
this.graph.addLink(newId, this.graph.InboxId);
|
||||||
|
this.word = "";
|
||||||
|
this.mode = "command";
|
||||||
|
} else {
|
||||||
|
this.word += event.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
display() {
|
||||||
|
switch (this.mode) {
|
||||||
|
case "insert":
|
||||||
|
return "insert> " + this.word;
|
||||||
|
case "command":
|
||||||
|
return "(i)nsert";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20625
js/d3.v7.js
vendored
Normal file
20625
js/d3.v7.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user