graf: nepouzivaji se primo data z tasks.json pro renderovani
This commit is contained in:
parent
7668b12abb
commit
2494eab6e7
6 changed files with 85 additions and 68 deletions
|
@ -1,41 +1,27 @@
|
|||
<script type="ts">
|
||||
import Graph from "./Graph.svelte";
|
||||
import { nonNull } from "./helpers";
|
||||
import { grabAssignment } from "./ksp-task-grabber";
|
||||
import type { TaskDescriptor, TasksFile } from "./task-loader";
|
||||
import { saveTasks, createTaskMap, getCategories } from "./task-loader";
|
||||
import { saveTasks, getCategories } from "./task-loader";
|
||||
import TaskDisplay from "./TaskDisplay.svelte";
|
||||
|
||||
export let tasks: TasksFile;
|
||||
|
||||
let selectedTask: string | null = null;
|
||||
let clickedTask: string | null = null;
|
||||
let repulsionForce: number = -600;
|
||||
let clicked: string[] = [];
|
||||
let graph: Graph;
|
||||
let currentTask: TaskDescriptor | null = null;
|
||||
|
||||
function clickTask(e: CustomEvent<TaskDescriptor>) {
|
||||
// sanity check
|
||||
if (selectedTask == null) {
|
||||
alert("tohle je divny event");
|
||||
return;
|
||||
}
|
||||
|
||||
// ukladani seznamu poslednich kliknuti
|
||||
clicked.push(selectedTask);
|
||||
clicked = [...clicked, e.detail.id];
|
||||
if (clicked.length > 3)
|
||||
clicked = clicked.slice(clicked.length - 3, clicked.length);
|
||||
clicked = clicked;
|
||||
|
||||
// trackovani, kam se naposledy kliknulo
|
||||
if (clickedTask == selectedTask) {
|
||||
clickedTask = null;
|
||||
} else {
|
||||
clickedTask = selectedTask;
|
||||
}
|
||||
}
|
||||
|
||||
$: taskMap = createTaskMap(tasks);
|
||||
$: currentTask = clickedTask != null ? clickedTask : selectedTask;
|
||||
function startHovering(e: CustomEvent<TaskDescriptor>) {
|
||||
currentTask = e.detail;
|
||||
}
|
||||
|
||||
function addEdge() {
|
||||
if (clicked.length > 1) {
|
||||
|
@ -44,21 +30,15 @@
|
|||
|
||||
tasks.tasks.forEach((t) => {
|
||||
if (t.id == first) {
|
||||
t.requires.push(second);
|
||||
t = t;
|
||||
t.requires = [...t.requires, second];
|
||||
}
|
||||
});
|
||||
tasks = tasks;
|
||||
|
||||
// run simulation
|
||||
graph.runSimulation()
|
||||
} else {
|
||||
alert("Nope, prvni musis nekam klikat...");
|
||||
}
|
||||
}
|
||||
|
||||
let graph: Graph
|
||||
|
||||
async function saveCurrentState() {
|
||||
await saveTasks(tasks);
|
||||
}
|
||||
|
@ -122,8 +102,8 @@
|
|||
<Graph
|
||||
{tasks}
|
||||
{repulsionForce}
|
||||
bind:selectedTask
|
||||
on:selectTask={clickTask}
|
||||
on:preSelectTask={startHovering}
|
||||
bind:this={graph} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -133,7 +113,7 @@
|
|||
<div>
|
||||
<button on:click={addEdge}>Pridat hranu - posledni vyzaduje predposledni</button>
|
||||
</div>
|
||||
<div><button on:click={toggleDivnaPromena}>Spustit simulaci</button></div>
|
||||
<div><button on:click={graph.runSimulation}>Spustit simulaci</button></div>
|
||||
<div>
|
||||
Repulsion force: <input type="number" bind:value={repulsionForce} name="repulsionForceInput" max="1000" min="-10000" />
|
||||
</div>
|
||||
|
@ -143,14 +123,14 @@
|
|||
</div>
|
||||
<div class="taskDetails">
|
||||
{#if currentTask != null}
|
||||
<h3>{currentTask}</h3>
|
||||
<span>{nonNull(taskMap.get(currentTask)).comment}</span>
|
||||
<h3>{currentTask.id}</h3>
|
||||
<span>{nonNull(currentTask).comment}</span>
|
||||
<ul>
|
||||
{#each getCategories(tasks, currentTask) as cat}
|
||||
{#each getCategories(tasks, currentTask.id) as cat}
|
||||
<li>{cat}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<TaskDisplay taskId={currentTask} />
|
||||
<TaskDisplay taskId={currentTask.id} />
|
||||
{:else}
|
||||
<h3>Nothing selected...</h3>
|
||||
{/if}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
import GraphEdge from "./GraphEdge.svelte";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import * as d3 from "d3";
|
||||
import { createLinksFromTaskMap } from "./task-loader";
|
||||
import type { TasksFile, TaskDescriptor } from "./task-loader";
|
||||
import { createNodesAndEdges } from "./graph-types";
|
||||
|
||||
export let tasks: TasksFile;
|
||||
let hoveredTask: null | string = null;
|
||||
|
@ -16,8 +16,17 @@
|
|||
let clientWidth: number;
|
||||
let svgElement: SVGElement;
|
||||
|
||||
$: nodes = tasks.tasks;
|
||||
$: edges = createLinksFromTaskMap(tasks);
|
||||
// this prevents svelte from updating nodes and edges
|
||||
// when we update nodes and edges
|
||||
let [nodes, edges] = createNodesAndEdges(tasks);
|
||||
function hack() {
|
||||
[nodes, edges] = createNodesAndEdges(tasks);
|
||||
runSimulation();
|
||||
}
|
||||
$: {
|
||||
tasks;
|
||||
hack();
|
||||
}
|
||||
|
||||
const eventDispatcher = createEventDispatcher();
|
||||
|
||||
|
@ -32,9 +41,8 @@
|
|||
hoveredTask = task.id;
|
||||
eventDispatcher("preSelectTask", task);
|
||||
} else {
|
||||
if (hoveredTask == task.id)
|
||||
hoveredTask = null;
|
||||
eventDispatcher("unPreSelectTask", task);
|
||||
if (hoveredTask == task.id) hoveredTask = null;
|
||||
eventDispatcher("unPreSelectTask", task);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -83,6 +91,8 @@
|
|||
}
|
||||
let zoomer = d3.zoom().scaleExtent([0.1, 2]).on("zoom", zoomed);
|
||||
d3.select(container).call(zoomer);
|
||||
|
||||
runSimulation();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -93,6 +103,8 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
{@debug tasks}
|
||||
|
||||
<div bind:this={container} bind:clientHeight bind:clientWidth>
|
||||
<svg bind:this={svgElement}>
|
||||
<g>
|
||||
|
@ -103,8 +115,8 @@
|
|||
<GraphNode
|
||||
{task}
|
||||
on:taskClick
|
||||
on:click={nodeClick(task)}
|
||||
on:hoveringChange={nodeHover(task)} />
|
||||
on:click={nodeClick(task.task)}
|
||||
on:hoveringChange={nodeHover(task.task)} />
|
||||
{/each}
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<script lang="ts">
|
||||
import type { SimulationLinkDatum } from "d3";
|
||||
import type { TaskId } from "./graph-types";
|
||||
|
||||
import type { TaskDescriptor } from "./task-loader";
|
||||
|
||||
export let edge: SimulationLinkDatum<TaskDescriptor>;
|
||||
export let edge: SimulationLinkDatum<TaskId>;
|
||||
|
||||
$: x1 = edge === undefined || edge.source === undefined || edge.source.x === undefined ? 0 : edge.source.x;
|
||||
$: y1 = edge === undefined || edge.source === undefined || edge.source.y === undefined ? 0 : edge.source.y;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import type { TaskDescriptor } from "./task-loader";
|
||||
import type { TaskId } from "./graph-types";
|
||||
|
||||
export let task: TaskDescriptor;
|
||||
|
||||
export let task: TaskId;
|
||||
let hovering: boolean = false;
|
||||
let text_element: SVGTextElement;
|
||||
|
||||
|
|
45
frontend/src/graph-types.ts
Normal file
45
frontend/src/graph-types.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import type { SimulationLinkDatum, SimulationNodeDatum } from "d3";
|
||||
import type { TaskDescriptor, TasksFile } from "./task-loader";
|
||||
import { createTaskMap } from "./task-loader";
|
||||
|
||||
|
||||
export type TaskId = {
|
||||
id: string;
|
||||
task: TaskDescriptor;
|
||||
} & SimulationNodeDatum;
|
||||
|
||||
function createNodes(tasks: TasksFile, old?: TaskId[]): TaskId[] {
|
||||
return tasks.tasks.map((t) => {
|
||||
return { id: t.id, task: t };
|
||||
});
|
||||
}
|
||||
|
||||
export function createNodesAndEdges(tasks: TasksFile, oldNodes?, oldEdges?): [TaskId[], SimulationLinkDatum<TaskId>[]] {
|
||||
let nodes = createNodes(tasks, oldNodes);
|
||||
|
||||
// create mapping from ID to node
|
||||
let nodeMap = new Map<string, TaskId>();
|
||||
for (let task of nodes) {
|
||||
if (task.id in nodeMap) throw 'duplicate IDs';
|
||||
nodeMap.set(task.id, task);
|
||||
}
|
||||
|
||||
let links: SimulationLinkDatum<TaskId>[] = [];
|
||||
for (const task of tasks.tasks) {
|
||||
const src = nodeMap.get(task.id)!;
|
||||
for (const id of task.requires) {
|
||||
const t = nodeMap.get(id);
|
||||
|
||||
if (t === undefined) throw `missing task with id ${id}`;
|
||||
|
||||
const l: SimulationLinkDatum<TaskId> =
|
||||
{
|
||||
source: src,
|
||||
target: t
|
||||
};
|
||||
links.push(l);
|
||||
}
|
||||
}
|
||||
|
||||
return [nodes, links];
|
||||
}
|
|
@ -4,8 +4,7 @@ export type TaskDescriptor = {
|
|||
id: string
|
||||
requires: string[]
|
||||
comment?: string
|
||||
} & SimulationNodeDatum
|
||||
|
||||
}
|
||||
|
||||
export type TasksFile = {
|
||||
tasks: TaskDescriptor[]
|
||||
|
@ -44,25 +43,6 @@ export function createTaskMap(tasks: TasksFile): TaskMap {
|
|||
return m;
|
||||
}
|
||||
|
||||
export function createLinksFromTaskMap(tasks: TasksFile): SimulationLinkDatum<TaskDescriptor>[] {
|
||||
let links: SimulationLinkDatum<TaskDescriptor>[] = [];
|
||||
|
||||
const taskMap = createTaskMap(tasks);
|
||||
|
||||
for (const task of tasks.tasks) {
|
||||
for (const id of task.requires) {
|
||||
const t = taskMap.get(id);
|
||||
|
||||
if (t === undefined) throw `missing task with id ${id}`;
|
||||
|
||||
const l: SimulationLinkDatum<TaskDescriptor> = { source: t, target: task };
|
||||
links.push(l);
|
||||
}
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
export function getCategories(tasks: TasksFile, taskId: string): string[] {
|
||||
let res: string[] = [];
|
||||
for (let [cat, ids] of Object.entries(tasks.clusters)) {
|
||||
|
|
Reference in a new issue