Browse Source

Less glitchy task expansion

mj-deploy
Standa Lukeš 4 years ago
parent
commit
debe92d440
  1. 2
      frontend/public/grafik.html
  2. 16
      frontend/src/App.svelte
  3. 12
      frontend/src/Editor.svelte
  4. 10
      frontend/src/Graph.svelte
  5. 23
      frontend/src/TaskDisplay.svelte
  6. 61
      frontend/src/TaskPanel.svelte
  7. 1
      frontend/src/helpers.ts
  8. 40
      frontend/src/ksp-task-grabber.ts

2
frontend/public/grafik.html

@ -14,6 +14,6 @@
</head>
<body style="padding: 0">
<div id="svelte-root"></div>
<div id="svelte-root" style="position: relative;"></div>
</body>
</html>

16
frontend/src/App.svelte

@ -5,15 +5,12 @@
import TasksLoader from "./TasksLoader.svelte";
import TaskPanel from "./TaskPanel.svelte";
import Editor from "./Editor.svelte";
import GraphEdge from "./GraphEdge.svelte";
import type { detach } from "svelte/internal";
const tasksPromise: Promise<TasksFile> = loadTasks();
let selectedTask: string | null = null;
let finalSelect: boolean = false;
function clickTask(e: CustomEvent<TaskDescriptor>) {
finalSelect = true;
}
let taskPanel: TaskPanel
// react to hash changes
let hash = window.location.hash.substr(1);
@ -51,9 +48,12 @@
</TasksLoader>
{:else}
<TasksLoader promise={tasksPromise} let:data={t}>
<TaskPanel bind:finalSelect {selectedTask} />
<TaskPanel bind:this={taskPanel} />
<div style="height: 100%">
<Graph tasks={t} bind:selectedTask on:selectTask={clickTask} />
<Graph tasks={t}
on:selectTask={e => taskPanel.select(e.detail)}
on:preSelectTask={e => taskPanel.preSelect(e.detail)}
on:unPreSelectTask={e => taskPanel.unPreselect(e.detail)} />
</div>
</TasksLoader>
{/if}

12
frontend/src/Editor.svelte

@ -1,8 +1,10 @@
<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 TaskDisplay from "./TaskDisplay.svelte";
export let tasks: TasksFile;
@ -145,19 +147,13 @@
<div class="taskDetails">
{#if currentTask != null}
<h3>{currentTask}</h3>
<span>{taskMap.get(currentTask).comment}</span>
<span>{nonNull(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>
<TaskDisplay task={currentTask} />
{:else}
<h3>Nothing selected...</h3>
{/if}

10
frontend/src/Graph.svelte

@ -7,7 +7,7 @@
import type { TasksFile, TaskDescriptor } from "./task-loader";
export let tasks: TasksFile;
export let selectedTask: null | string = null;
let hoveredTask: null | string = null;
export let repulsionForce: number = -600;
// Svelte automatically fills these with a reference
@ -22,7 +22,6 @@
const eventDispatcher = createEventDispatcher();
const nodeClick = (task: TaskDescriptor) => (e: CustomEvent<MouseEvent>) => {
selectedTask = task.id;
eventDispatcher("selectTask", task);
};
@ -30,9 +29,12 @@
hovering: CustomEvent<boolean>
) => {
if (hovering.detail) {
selectedTask = task.id;
hoveredTask = task.id;
eventDispatcher("preSelectTask", task);
} else {
if (selectedTask == task.id) selectedTask = null;
if (hoveredTask == task.id)
hoveredTask = null;
eventDispatcher("unPreSelectTask", task);
}
};

23
frontend/src/TaskDisplay.svelte

@ -0,0 +1,23 @@
<script type="ts">
import { grabAssignment } from "./ksp-task-grabber";
import { nonNull } from './helpers'
export let task: string | null | undefined
</script>
<style>
div {
text-align: justify;
}
</style>
<div>
{#if task != null}
{#await grabAssignment(nonNull(task))}
Načítám úlohu
{:then task}
{@html task.titleHtml}
<hr>
{@html task.description}
<div class="clearfloat" />
{/await}
{/if}
</div>

61
frontend/src/TaskPanel.svelte

@ -1,42 +1,67 @@
<script lang="ts">
import { grabAssignment } from "./ksp-task-grabber";
import type { TasksFile, TaskDescriptor } from "./task-loader";
import TaskDisplay from "./TaskDisplay.svelte";
// export let tasks: TasksFile;
export let selectedTask: string | null = null
export let finalSelect: boolean = false
let mouse: boolean = false
let selectedTask: TaskDescriptor | null = null
let height: string;
$: height = selectedTask == null && !mouse ? "0" :
finalSelect ? "100%" :
"100px"
let heightClass: "collapsed" | "full" | "preview" = "collapsed"
let taskPromise: Promise<string | null>
export function preSelect(task: TaskDescriptor) {
if (heightClass != "full") {
selectedTask = task
heightClass = "preview"
}
}
export function unPreselect(task: TaskDescriptor) {
setTimeout(() => {
if (selectedTask && task.id == selectedTask.id && heightClass != "full") {
heightClass = "collapsed"
}
}, 10);
}
export function select(task: TaskDescriptor) {
selectedTask = task
heightClass = "full"
}
$: {
if (selectedTask != null)
taskPromise = grabAssignment(selectedTask)
taskPromise = grabAssignment(selectedTask.id)
}
</script>
<style>
.panel {
position: absolute;
width: 100%;
width: calc(100%-200px);
background-color: #222;
overflow: hidden;
padding: 0 100px 0 100px;
}
.panel.collapsed:not(:hover) {
display: none;
}
.panel.preview, .panel:not(.full):hover {
height: 100px;
}
.panel.full {
min-height: 100%;
}
.closeButton { display: none }
.panel.full .closeButton { display: inherit }
</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}>
<div class="panel {heightClass}"
on:click={() => selectedTask && select(selectedTask)}>
<TaskDisplay task={selectedTask?.id} />
<button type=button class="closeButton" on:click|stopPropagation={() => heightClass = "collapsed"}>
Zavřít
</button>
{/if}
</div>

1
frontend/src/helpers.ts

@ -0,0 +1 @@
export function nonNull<T>(a: T | null | undefined): T { return a! }

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

@ -1,8 +1,9 @@
export type TaskAssignmentData = {
id: string,
name: string,
points: number,
description: HTMLElement
points: number | null,
description: string,
titleHtml: string
}
type TaskLocation = {
@ -31,7 +32,7 @@ function getLocation(id: string, solution: boolean): TaskLocation | null {
}
}
function parseTask(startElementId: string, html: string): string {
function parseTask(startElementId: string, html: string): TaskAssignmentData {
const parser = new DOMParser()
const doc = parser.parseFromString(html, "text/html")
@ -42,6 +43,13 @@ function parseTask(startElementId: string, html: string): string {
let e = titleElement
const titleMatch = /(\d-Z?\d+-\d+) (.*?)( \((\d+) bod.*\))?/.exec(e.innerText.trim())
if (!titleMatch) {
var [_, id, name, _, points] = ["", startElementId, "Neznámé jméno úlohy", "", ""]
} else {
var [_, id, name, _, points] = titleMatch
}
while (e.nextElementSibling &&
e.nextElementSibling?.tagName.toLowerCase() == "hr")
e = e.nextElementSibling as HTMLElement
@ -60,7 +68,13 @@ function parseTask(startElementId: string, html: string): string {
let r = ""
for (const e of elements)
r += e.outerHTML + "\n"
return r
return {
description: r,
id: id.trim(),
name: name.trim(),
points: points ? +points : null,
titleHtml: titleElement.outerHTML
}
}
async function loadTask({ url, startElement }: TaskLocation) {
@ -72,14 +86,24 @@ async function loadTask({ url, startElement }: TaskLocation) {
return parseTask(startElement, rText)
}
export async function grabAssignment(id: string): Promise<string> {
function virtualTask(id: string): TaskAssignmentData {
return {
id,
description: "úloha je virtuální a neexistuje",
name: id,
points: 0,
titleHtml: "<h3>Virtuální úloha</h3>"
}
}
export async function grabAssignment(id: string): Promise<TaskAssignmentData> {
const l = getLocation(id, false)
if (!l) return "úloha je virtuální a neexistuje"
if (!l) return virtualTask(id)
return await loadTask(l)
}
export async function grabSolution(id: string) {
export async function grabSolution(id: string): Promise<TaskAssignmentData> {
const l = getLocation(id, true)
if (!l) return "úloha je virtuální a neexistuje"
if (!l) return virtualTask(id)
return await loadTask(l)
}