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