Crippled version of task display
This commit is contained in:
parent
5298dae17f
commit
6ec2234ef9
5 changed files with 100 additions and 20 deletions
|
@ -2,10 +2,19 @@
|
||||||
import Graph from "./Graph.svelte";
|
import Graph from "./Graph.svelte";
|
||||||
import GraphNode from "./GraphNode.svelte";
|
import GraphNode from "./GraphNode.svelte";
|
||||||
import { loadTasks } from "./task-loader";
|
import { loadTasks } from "./task-loader";
|
||||||
import type { TasksFile } from "./task-loader";
|
import type { TasksFile, TaskDescriptor } from "./task-loader";
|
||||||
import TasksLoader from "./TasksLoader.svelte";
|
import TasksLoader from "./TasksLoader.svelte";
|
||||||
|
import TaskPanel from "./TaskPanel.svelte";
|
||||||
|
|
||||||
const tasksPromise: Promise<TasksFile> = loadTasks();
|
const tasksPromise: Promise<TasksFile> = loadTasks();
|
||||||
|
|
||||||
|
let selectedTask: string | null = null
|
||||||
|
let finalSelect: boolean = false
|
||||||
|
|
||||||
|
function clickTask(e: CustomEvent<TaskDescriptor>) {
|
||||||
|
finalSelect = true
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -37,6 +46,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
-->
|
-->
|
||||||
<TasksLoader promise={tasksPromise} let:data={t}>
|
<TasksLoader promise={tasksPromise} let:data={t}>
|
||||||
<Graph tasks={t} />
|
<TaskPanel bind:finalSelect={finalSelect} selectedTask={selectedTask} />
|
||||||
|
<Graph tasks={t} bind:selectedTask={selectedTask} on:selectTask={clickTask} />
|
||||||
</TasksLoader>
|
</TasksLoader>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -1,15 +1,32 @@
|
||||||
<script type="ts">
|
<script type="ts">
|
||||||
import GraphNode from "./GraphNode.svelte";
|
import GraphNode from "./GraphNode.svelte";
|
||||||
import GraphEdge from "./GraphEdge.svelte";
|
import GraphEdge from "./GraphEdge.svelte";
|
||||||
import { onMount } from "svelte";
|
import { createEventDispatcher, onMount } from "svelte";
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import { createLinksFromTaskMap } from "./task-loader";
|
import { createLinksFromTaskMap } from "./task-loader";
|
||||||
import type { TasksFile } from "./task-loader";
|
import type { TasksFile, TaskDescriptor } from "./task-loader";
|
||||||
|
|
||||||
|
const eventDispatcher = createEventDispatcher()
|
||||||
|
|
||||||
export let tasks: TasksFile;
|
export let tasks: TasksFile;
|
||||||
let nodes = tasks.tasks;
|
let nodes = tasks.tasks;
|
||||||
let edges = createLinksFromTaskMap(tasks);
|
let edges = createLinksFromTaskMap(tasks);
|
||||||
|
|
||||||
|
export let selectedTask: null | string = null
|
||||||
|
const nodeClick = (task: TaskDescriptor) => (e: CustomEvent<MouseEvent>) => {
|
||||||
|
selectedTask = task.id
|
||||||
|
eventDispatcher("selectTask", task)
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeHover = (task: TaskDescriptor) => (hovering: CustomEvent<boolean>) => {
|
||||||
|
if (hovering.detail) {
|
||||||
|
selectedTask = task.id
|
||||||
|
} else {
|
||||||
|
if (selectedTask == task.id)
|
||||||
|
selectedTask = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Svelte automatically fills this with a reference
|
// Svelte automatically fills this with a reference
|
||||||
let container: HTMLElement;
|
let container: HTMLElement;
|
||||||
|
|
||||||
|
@ -69,7 +86,7 @@
|
||||||
<GraphEdge {edge} />
|
<GraphEdge {edge} />
|
||||||
{/each}
|
{/each}
|
||||||
{#each nodes as task}
|
{#each nodes as task}
|
||||||
<GraphNode {task} />
|
<GraphNode {task} on:click={nodeClick(task)} on:hoveringChange={nodeHover(task)} />
|
||||||
{/each}
|
{/each}
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { createEventDispatcher, onMount } from "svelte";
|
||||||
|
|
||||||
import type { TaskDescriptor } from "./task-loader";
|
import type { TaskDescriptor } from "./task-loader";
|
||||||
|
|
||||||
|
@ -7,15 +7,23 @@
|
||||||
let hovering: boolean = false;
|
let hovering: boolean = false;
|
||||||
let text_element: SVGTextElement;
|
let text_element: SVGTextElement;
|
||||||
|
|
||||||
|
const eventDispatcher = createEventDispatcher()
|
||||||
|
|
||||||
$: cx = task === undefined || task.x === undefined ? 0 : task.x;
|
$: cx = task === undefined || task.x === undefined ? 0 : task.x;
|
||||||
$: cy = task === undefined || task.y === undefined ? 0 : task.y;
|
$: cy = task === undefined || task.y === undefined ? 0 : task.y;
|
||||||
|
|
||||||
function enter() {
|
function enter() {
|
||||||
hovering = true;
|
hovering = true;
|
||||||
|
eventDispatcher("hoveringChange", hovering)
|
||||||
}
|
}
|
||||||
|
|
||||||
function leave() {
|
function leave() {
|
||||||
hovering = false;
|
hovering = false;
|
||||||
|
eventDispatcher("hoveringChange", hovering)
|
||||||
|
}
|
||||||
|
|
||||||
|
function click(e: MouseEvent) {
|
||||||
|
eventDispatcher("click", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
let ellipse_rx = 20;
|
let ellipse_rx = 20;
|
||||||
|
@ -36,7 +44,7 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<g on:mouseenter={enter} on:mouseleave={leave}>
|
<g on:mouseenter={enter} on:mouseleave={leave} on:click={click}>
|
||||||
<ellipse rx={ellipse_rx} ry={ellipse_ry} {cx} {cy} />
|
<ellipse rx={ellipse_rx} ry={ellipse_ry} {cx} {cy} />
|
||||||
<text
|
<text
|
||||||
bind:this={text_element}
|
bind:this={text_element}
|
||||||
|
|
42
frontend/src/TaskPanel.svelte
Normal file
42
frontend/src/TaskPanel.svelte
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { grabAssignment } from "./ksp-task-grabber";
|
||||||
|
import type { TasksFile, TaskDescriptor } from "./task-loader";
|
||||||
|
|
||||||
|
// export let tasks: TasksFile;
|
||||||
|
export let selectedTask: string | null = null
|
||||||
|
export let finalSelect: boolean = false
|
||||||
|
let mouse: boolean = false
|
||||||
|
|
||||||
|
let height: string;
|
||||||
|
$: height = selectedTask == null && !mouse ? "0" :
|
||||||
|
finalSelect ? "100%" :
|
||||||
|
"100px"
|
||||||
|
|
||||||
|
let taskPromise: Promise<string | null>
|
||||||
|
$: {
|
||||||
|
if (selectedTask != null)
|
||||||
|
taskPromise = grabAssignment(selectedTask)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.panel {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #222;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="panel" style="height: {height}" on:mouseover={() => mouse = false} on:mouseout={() => mouse = false}>
|
||||||
|
{#if selectedTask != null}
|
||||||
|
{#await taskPromise}
|
||||||
|
Načítám úložku {selectedTask} ;)
|
||||||
|
{:then task}
|
||||||
|
{@html task}
|
||||||
|
{/await}
|
||||||
|
<button type=button on:click={() => finalSelect = false}>
|
||||||
|
Zavřít
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
|
@ -12,28 +12,28 @@ type TaskLocation = {
|
||||||
startElement: string
|
startElement: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLocation(id: string, solution: boolean): TaskLocation {
|
function getLocation(id: string, solution: boolean): TaskLocation | null {
|
||||||
const m = /^(\d+)-(Z?)(\d)-(\d)$/.exec(id)
|
const m = /^(\d+)-(Z?)(\d)-(\d)$/.exec(id)
|
||||||
if (!m) throw new Error(`Invalid task id: ${m}`)
|
if (!m) return null
|
||||||
const [_, rocnik, z, serie, uloha] = m[1]
|
const [_, rocnik, z, serie, uloha] = m
|
||||||
if (z == 'Z') {
|
if (z == 'Z') {
|
||||||
const urlX = solution ? "reseni" : "zadani"
|
const urlX = solution ? "reseni" : "zadani"
|
||||||
return {
|
return {
|
||||||
url: `z/ulohy/${rocnik}/${urlX}${serie}.html`,
|
url: `z/ulohy/${rocnik}/${urlX}${serie}.html`,
|
||||||
startElement: `task${uloha}`
|
startElement: `task-${id}`
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const urlX = solution ? "solution" : "tasks"
|
const urlX = solution ? "solution" : "tasks"
|
||||||
return {
|
return {
|
||||||
url: `tasks/${rocnik}/${urlX}${serie}.html`,
|
url: `tasks/${rocnik}/${urlX}${serie}.html`,
|
||||||
startElement: `task${uloha}`
|
startElement: `task-${id}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTask(startElementId: string, html: string, contentType: string): string {
|
function parseTask(startElementId: string, html: string): string {
|
||||||
const parser = new DOMParser()
|
const parser = new DOMParser()
|
||||||
const doc = parser.parseFromString(html, contentType as any)
|
const doc = parser.parseFromString(html, "text/html")
|
||||||
|
|
||||||
const titleElement = doc.getElementById(startElementId)
|
const titleElement = doc.getElementById(startElementId)
|
||||||
if (!titleElement)
|
if (!titleElement)
|
||||||
|
@ -69,14 +69,17 @@ async function loadTask({ url, startElement }: TaskLocation) {
|
||||||
throw Error("Bad request")
|
throw Error("Bad request")
|
||||||
}
|
}
|
||||||
const rText = await r.text()
|
const rText = await r.text()
|
||||||
const contentType = r.headers.get("Content-Type") || "text/html"
|
return parseTask(startElement, rText)
|
||||||
return parseTask(startElement, rText, contentType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadAssignment(id: string) {
|
export async function grabAssignment(id: string) {
|
||||||
return loadTask(getLocation(id, false))
|
const l = getLocation(id, false)
|
||||||
|
if (!l) return "úloha je virtuální a neexistuje"
|
||||||
|
return await loadTask(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadSolution(id: string) {
|
export async function grabSolution(id: string) {
|
||||||
return loadTask(getLocation(id, true))
|
const l = getLocation(id, true)
|
||||||
|
if (!l) return "úloha je virtuální a neexistuje"
|
||||||
|
return await loadTask(l)
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue