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"> <script type="ts">
import Graph from "./Graph.svelte"; import Graph from "./Graph.svelte";
import { nonNull } from "./helpers"; import { nonNull } from "./helpers";
import { grabAssignment } from "./ksp-task-grabber";
import type { TaskDescriptor, TasksFile } from "./task-loader"; 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"; import TaskDisplay from "./TaskDisplay.svelte";
export let tasks: TasksFile; export let tasks: TasksFile;
let selectedTask: string | null = null;
let clickedTask: string | null = null;
let repulsionForce: number = -600; let repulsionForce: number = -600;
let clicked: string[] = []; let clicked: string[] = [];
let graph: Graph;
let currentTask: TaskDescriptor | null = null;
function clickTask(e: CustomEvent<TaskDescriptor>) { function clickTask(e: CustomEvent<TaskDescriptor>) {
// sanity check
if (selectedTask == null) {
alert("tohle je divny event");
return;
}
// ukladani seznamu poslednich kliknuti // ukladani seznamu poslednich kliknuti
clicked.push(selectedTask); clicked = [...clicked, e.detail.id];
if (clicked.length > 3) if (clicked.length > 3)
clicked = clicked.slice(clicked.length - 3, clicked.length); 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); function startHovering(e: CustomEvent<TaskDescriptor>) {
$: currentTask = clickedTask != null ? clickedTask : selectedTask; currentTask = e.detail;
}
function addEdge() { function addEdge() {
if (clicked.length > 1) { if (clicked.length > 1) {
@ -44,21 +30,15 @@
tasks.tasks.forEach((t) => { tasks.tasks.forEach((t) => {
if (t.id == first) { if (t.id == first) {
t.requires.push(second); t.requires = [...t.requires, second];
t = t;
} }
}); });
tasks = tasks; tasks = tasks;
// run simulation
graph.runSimulation()
} else { } else {
alert("Nope, prvni musis nekam klikat..."); alert("Nope, prvni musis nekam klikat...");
} }
} }
let graph: Graph
async function saveCurrentState() { async function saveCurrentState() {
await saveTasks(tasks); await saveTasks(tasks);
} }
@ -122,8 +102,8 @@
<Graph <Graph
{tasks} {tasks}
{repulsionForce} {repulsionForce}
bind:selectedTask
on:selectTask={clickTask} on:selectTask={clickTask}
on:preSelectTask={startHovering}
bind:this={graph} /> bind:this={graph} />
</div> </div>
</div> </div>
@ -133,7 +113,7 @@
<div> <div>
<button on:click={addEdge}>Pridat hranu - posledni vyzaduje predposledni</button> <button on:click={addEdge}>Pridat hranu - posledni vyzaduje predposledni</button>
</div> </div>
<div><button on:click={toggleDivnaPromena}>Spustit simulaci</button></div> <div><button on:click={graph.runSimulation}>Spustit simulaci</button></div>
<div> <div>
Repulsion force: <input type="number" bind:value={repulsionForce} name="repulsionForceInput" max="1000" min="-10000" /> Repulsion force: <input type="number" bind:value={repulsionForce} name="repulsionForceInput" max="1000" min="-10000" />
</div> </div>
@ -143,14 +123,14 @@
</div> </div>
<div class="taskDetails"> <div class="taskDetails">
{#if currentTask != null} {#if currentTask != null}
<h3>{currentTask}</h3> <h3>{currentTask.id}</h3>
<span>{nonNull(taskMap.get(currentTask)).comment}</span> <span>{nonNull(currentTask).comment}</span>
<ul> <ul>
{#each getCategories(tasks, currentTask) as cat} {#each getCategories(tasks, currentTask.id) as cat}
<li>{cat}</li> <li>{cat}</li>
{/each} {/each}
</ul> </ul>
<TaskDisplay taskId={currentTask} /> <TaskDisplay taskId={currentTask.id} />
{:else} {:else}
<h3>Nothing selected...</h3> <h3>Nothing selected...</h3>
{/if} {/if}

28
frontend/src/Graph.svelte

@ -3,8 +3,8 @@
import GraphEdge from "./GraphEdge.svelte"; import GraphEdge from "./GraphEdge.svelte";
import { createEventDispatcher, onMount } from "svelte"; import { createEventDispatcher, onMount } from "svelte";
import * as d3 from "d3"; import * as d3 from "d3";
import { createLinksFromTaskMap } from "./task-loader";
import type { TasksFile, TaskDescriptor } from "./task-loader"; import type { TasksFile, TaskDescriptor } from "./task-loader";
import { createNodesAndEdges } from "./graph-types";
export let tasks: TasksFile; export let tasks: TasksFile;
let hoveredTask: null | string = null; let hoveredTask: null | string = null;
@ -16,8 +16,17 @@
let clientWidth: number; let clientWidth: number;
let svgElement: SVGElement; let svgElement: SVGElement;
$: nodes = tasks.tasks; // this prevents svelte from updating nodes and edges
$: edges = createLinksFromTaskMap(tasks); // when we update nodes and edges
let [nodes, edges] = createNodesAndEdges(tasks);
function hack() {
[nodes, edges] = createNodesAndEdges(tasks);
runSimulation();
}
$: {
tasks;
hack();
}
const eventDispatcher = createEventDispatcher(); const eventDispatcher = createEventDispatcher();
@ -32,9 +41,8 @@
hoveredTask = task.id; hoveredTask = task.id;
eventDispatcher("preSelectTask", task); eventDispatcher("preSelectTask", task);
} else { } else {
if (hoveredTask == task.id) if (hoveredTask == task.id) hoveredTask = null;
hoveredTask = null; eventDispatcher("unPreSelectTask", task);
eventDispatcher("unPreSelectTask", task);
} }
}; };
@ -83,6 +91,8 @@
} }
let zoomer = d3.zoom().scaleExtent([0.1, 2]).on("zoom", zoomed); let zoomer = d3.zoom().scaleExtent([0.1, 2]).on("zoom", zoomed);
d3.select(container).call(zoomer); d3.select(container).call(zoomer);
runSimulation();
}); });
</script> </script>
@ -93,6 +103,8 @@
} }
</style> </style>
{@debug tasks}
<div bind:this={container} bind:clientHeight bind:clientWidth> <div bind:this={container} bind:clientHeight bind:clientWidth>
<svg bind:this={svgElement}> <svg bind:this={svgElement}>
<g> <g>
@ -103,8 +115,8 @@
<GraphNode <GraphNode
{task} {task}
on:taskClick on:taskClick
on:click={nodeClick(task)} on:click={nodeClick(task.task)}
on:hoveringChange={nodeHover(task)} /> on:hoveringChange={nodeHover(task.task)} />
{/each} {/each}
</g> </g>
</svg> </svg>

5
frontend/src/GraphEdge.svelte

@ -1,9 +1,8 @@
<script lang="ts"> <script lang="ts">
import type { SimulationLinkDatum } from "d3"; import type { SimulationLinkDatum } from "d3";
import type { TaskId } from "./graph-types";
import type { TaskDescriptor } from "./task-loader"; export let edge: SimulationLinkDatum<TaskId>;
export let edge: SimulationLinkDatum<TaskDescriptor>;
$: x1 = edge === undefined || edge.source === undefined || edge.source.x === undefined ? 0 : edge.source.x; $: 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; $: 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"> <script lang="ts">
import { createEventDispatcher, onMount } from "svelte"; 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 hovering: boolean = false;
let text_element: SVGTextElement; 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 id: string
requires: string[] requires: string[]
comment?: string comment?: string
} & SimulationNodeDatum }
export type TasksFile = { export type TasksFile = {
tasks: TaskDescriptor[] tasks: TaskDescriptor[]
@ -44,25 +43,6 @@ export function createTaskMap(tasks: TasksFile): TaskMap {
return m; 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[] { export function getCategories(tasks: TasksFile, taskId: string): string[] {
let res: string[] = []; let res: string[] = [];
for (let [cat, ids] of Object.entries(tasks.clusters)) { for (let [cat, ids] of Object.entries(tasks.clusters)) {