Browse Source

graf: nepouzivaji se primo data z tasks.json pro renderovani

mj-deploy
Vašek Šraier 4 years ago
parent
commit
2494eab6e7
  1. 48
      frontend/src/Editor.svelte
  2. 28
      frontend/src/Graph.svelte
  3. 5
      frontend/src/GraphEdge.svelte
  4. 5
      frontend/src/GraphNode.svelte
  5. 45
      frontend/src/graph-types.ts
  6. 22
      frontend/src/task-loader.ts

48
frontend/src/Editor.svelte

@ -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}

28
frontend/src/Graph.svelte

@ -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>

5
frontend/src/GraphEdge.svelte

@ -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;

5
frontend/src/GraphNode.svelte

@ -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

@ -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];
}

22
frontend/src/task-loader.ts

@ -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)) {