Browse Source

editor: first version

mj-deploy
Vašek Šraier 4 years ago
parent
commit
2006216a99
  1. 2
      frontend/public/grafik.html
  2. 24
      frontend/src/App.svelte
  3. 143
      frontend/src/Editor.svelte
  4. 83
      frontend/src/Graph.svelte
  5. 2
      frontend/src/ksp-task-grabber.ts
  6. 11
      frontend/src/task-loader.ts

2
frontend/public/grafik.html

@ -13,7 +13,7 @@
<script type="module" src='/build/bundle.js'></script>
</head>
<body>
<body style="padding: 0">
<div id="svelte-root"></div>
</body>
</html>

24
frontend/src/App.svelte

@ -1,10 +1,10 @@
<script lang="ts">
import Graph from "./Graph.svelte";
import GraphNode from "./GraphNode.svelte";
import { loadTasks } from "./task-loader";
import type { TasksFile, TaskDescriptor } from "./task-loader";
import TasksLoader from "./TasksLoader.svelte";
import TaskPanel from "./TaskPanel.svelte";
import Editor from "./Editor.svelte";
const tasksPromise: Promise<TasksFile> = loadTasks();
@ -15,12 +15,13 @@
finalSelect = true
}
const hash = window.location.hash.substr(1);
</script>
<style>
main {
text-align: center;
padding: 1em;
max-width: 240px;
margin: 0 auto;
}
@ -40,13 +41,14 @@
</style>
<main>
<!--
<svg height=200 width=200>
<GraphNode task="null" />
</svg>
-->
<TasksLoader promise={tasksPromise} let:data={t}>
<TaskPanel bind:finalSelect={finalSelect} selectedTask={selectedTask} />
<Graph tasks={t} bind:selectedTask={selectedTask} on:selectTask={clickTask} />
</TasksLoader>
{#if hash == "editor"}
<TasksLoader promise={tasksPromise} let:data={t}>
<Editor tasks={t} />
</TasksLoader>
{:else}
<TasksLoader promise={tasksPromise} let:data={t}>
<TaskPanel bind:finalSelect={finalSelect} selectedTask={selectedTask} />
<Graph tasks={t} bind:selectedTask={selectedTask} on:selectTask={clickTask} />
</TasksLoader>
{/if}
</main>

143
frontend/src/Editor.svelte

@ -0,0 +1,143 @@
<script type="ts">
import Graph from "./Graph.svelte";
import { grabAssignment } from "./ksp-task-grabber";
import type { TaskDescriptor, TasksFile } from "./task-loader";
import { createTaskMap, getCategories } from "./task-loader";
export let tasks: TasksFile;
let selectedTask: string | null = null;
let clickedTask: string | null = null;
let clicked: string[] = [];
function clickTask(e: CustomEvent<TaskDescriptor>) {
// ukladani seznamu poslednich kliknuti
clicked.push(selectedTask);
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 addEdge() {
if (clicked.length > 1) {
const first = clicked[clicked.length - 1];
const second = clicked[clicked.length - 2];
tasks.tasks.forEach((t) => {
if (t.id == first) {
t.requires.push(second);
t = t;
}
});
tasks = tasks;
} else {
alert("Nope, prvni musis nekam klikat...");
}
}
let hovnoDivnaPromenaKteraJeFaktFuj = true;
function toggleDivnaPromena() {
hovnoDivnaPromenaKteraJeFaktFuj = ! hovnoDivnaPromenaKteraJeFaktFuj;
}
</script>
<style>
h3 {
margin: 0;
padding: 0;
}
.container {
display: flex;
flex-direction: row;
margin: 0;
height: 100vh;
width: 100%;
}
.graph {
width: 100%;
margin: 0;
background-color: black;
height: 95%;
}
.right {
width: 40vw;
height: 100%;
}
.left {
width: 60vw;
height: 100%;
}
.toolbox {
width: 100%;
margin: 0;
height: 50%;
background-color: pink;
}
.taskDetails {
width: 100%;
margin: 0;
height: 50%;
background-color: aqua;
}
.lastClicked {
height: 5%;
}
</style>
<div class="container">
<div class="left">
<div class="lastClicked">Last clicked: <b>{clicked.join(' | ')}</b></div>
<div class="graph">
<Graph {tasks} bind:selectedTask on:selectTask={clickTask} runSimulationWeirdHack={hovnoDivnaPromenaKteraJeFaktFuj} />
</div>
</div>
<div class="right">
<div class="toolbox">
<div>Toolbox</div>
<div>
<button on:click={addEdge}>Pridat hranu - posledni vyzaduje predposledni</button>
</div>
<div>
<button on:click={toggleDivnaPromena}>Spustit simulaci</button>
</div>
</div>
<div class="taskDetails">
{#if currentTask != null}
<h3>{currentTask}</h3>
<span>{taskMap.get(currentTask).comment}</span>
<ul>
{#each getCategories(tasks, currentTask) as cat}
<li>{cat}</li>
{/each}
</ul>
<div>
{#await grabAssignment(currentTask)}
Loading...
{:then text}
{@html text}
{/await}
</div>
{:else}
<h3>Nothing selected...</h3>
{/if}
</div>
</div>
</div>

83
frontend/src/Graph.svelte

@ -6,48 +6,35 @@
import { createLinksFromTaskMap } from "./task-loader";
import type { TasksFile, TaskDescriptor } from "./task-loader";
const eventDispatcher = createEventDispatcher()
const eventDispatcher = createEventDispatcher();
export let tasks: TasksFile;
let nodes = tasks.tasks;
let edges = createLinksFromTaskMap(tasks);
export let selectedTask: null | string = null;
$: nodes = tasks.tasks;
$: edges = createLinksFromTaskMap(tasks);
export let selectedTask: null | string = null
const nodeClick = (task: TaskDescriptor) => (e: CustomEvent<MouseEvent>) => {
selectedTask = task.id
eventDispatcher("selectTask", task)
}
selectedTask = task.id;
eventDispatcher("selectTask", task);
};
const nodeHover = (task: TaskDescriptor) => (hovering: CustomEvent<boolean>) => {
const nodeHover = (task: TaskDescriptor) => (
hovering: CustomEvent<boolean>
) => {
if (hovering.detail) {
selectedTask = task.id
selectedTask = task.id;
} else {
if (selectedTask == task.id)
selectedTask = null
if (selectedTask == task.id) selectedTask = null;
}
}
};
// Svelte automatically fills this with a reference
let container: HTMLElement;
onMount(async () => {
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 40 },
width = container.clientWidth - margin.left - margin.right,
height = container.clientHeight - margin.top - margin.bottom;
// resize the svg object
var svg = d3
.select(container)
.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.select("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
function runSimulation() {
// Let's list the force we wanna apply on the network
var simulation = d3
let simulation = d3
.forceSimulation(nodes) // Force algorithm is applied to data.nodes
.force(
"link",
@ -69,12 +56,41 @@
edges = edges;
nodes = nodes;
}
}
// run on create
onMount(() => {
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 40 },
width = container.clientWidth - margin.left - margin.right,
height = container.clientHeight - margin.top - margin.bottom;
// resize the svg object
var svg = d3
.select(container)
.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.select("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
runSimulation();
});
// don't forget to vomit 🤮🤢
export let runSimulationWeirdHack: boolean = true;
$: {
runSimulationWeirdHack;
runSimulation();
}
// now it's safe to stop vomitting 🤮
</script>
<style>
div {
height: 100vh;
height: 100%;
width: 100%;
}
</style>
@ -83,10 +99,13 @@
<svg>
<g>
{#each edges as edge}
<GraphEdge {edge} />
<GraphEdge {edge} />
{/each}
{#each nodes as task}
<GraphNode {task} on:click={nodeClick(task)} on:hoveringChange={nodeHover(task)} />
<GraphNode
{task}
on:click={nodeClick(task)}
on:hoveringChange={nodeHover(task)} />
{/each}
</g>
</svg>

2
frontend/src/ksp-task-grabber.ts

@ -72,7 +72,7 @@ async function loadTask({ url, startElement }: TaskLocation) {
return parseTask(startElement, rText)
}
export async function grabAssignment(id: string) {
export async function grabAssignment(id: string): Promise<string> {
const l = getLocation(id, false)
if (!l) return "úloha je virtuální a neexistuje"
return await loadTask(l)

11
frontend/src/task-loader.ts

@ -2,7 +2,7 @@ import type { SimulationNodeDatum, SimulationLinkDatum } from "d3";
export type TaskDescriptor = {
id: string
requires: []
requires: string[]
comment?: string
} & SimulationNodeDatum
@ -49,3 +49,12 @@ export function createLinksFromTaskMap(tasks: TasksFile): SimulationLinkDatum<Ta
return links;
}
export function getCategories(tasks: TasksFile, taskId: string): string[] {
let res: string[] = [];
for (let [cat, ids] of Object.entries(tasks.clusters)) {
if (ids.indexOf(taskId) >= 0) res.push(cat);
}
return res;
}