graph: option to hide nodes
This commit is contained in:
parent
a532632857
commit
96dccd779d
6 changed files with 365 additions and 161 deletions
|
@ -68,7 +68,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// autosave ;)
|
// autosave ;)
|
||||||
let saveTimeoutHandle: number | null = null;
|
let saveTimeoutHandle: NodeJS.Timeout | null = null;
|
||||||
function autosave() {
|
function autosave() {
|
||||||
if (saveTimeoutHandle != null) clearTimeout(saveTimeoutHandle);
|
if (saveTimeoutHandle != null) clearTimeout(saveTimeoutHandle);
|
||||||
|
|
||||||
|
@ -77,7 +77,10 @@
|
||||||
await saveTasks(tasks);
|
await saveTasks(tasks);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
$: { tasks; autosave(); };
|
$: {
|
||||||
|
tasks;
|
||||||
|
autosave();
|
||||||
|
}
|
||||||
|
|
||||||
function saveLocally() {
|
function saveLocally() {
|
||||||
saveToLocalDisk("tasks.json", tasksToString(tasks));
|
saveToLocalDisk("tasks.json", tasksToString(tasks));
|
||||||
|
@ -197,6 +200,20 @@
|
||||||
}));
|
}));
|
||||||
tasks.tasks = [...tasks.tasks, ...newDescriptors];
|
tasks.tasks = [...tasks.tasks, ...newDescriptors];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hideSelection() {
|
||||||
|
for (let t of graph.getCurrentSelection()) {
|
||||||
|
t.hidden = true;
|
||||||
|
}
|
||||||
|
tasks = tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSelection() {
|
||||||
|
for (let t of graph.getCurrentSelection()) {
|
||||||
|
t.hidden = false;
|
||||||
|
}
|
||||||
|
tasks = tasks;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -297,7 +314,8 @@
|
||||||
bind:this={graph}
|
bind:this={graph}
|
||||||
{nodeDraggingEnabled}
|
{nodeDraggingEnabled}
|
||||||
on:openTask={openTaskDetailEditorButton}
|
on:openTask={openTaskDetailEditorButton}
|
||||||
{showHiddenEdges} />
|
{showHiddenEdges}
|
||||||
|
showHidden={true} />
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="topLeftHint">
|
<div class="topLeftHint">
|
||||||
|
@ -358,6 +376,10 @@
|
||||||
title={isLoggedIn() ? 'Nahraje všechny úlohy z jednoho ročníku, které tu ještě nejsou' : 'Je nutné být přihlášený a na stránce v KSP template.'}>Nahrát
|
title={isLoggedIn() ? 'Nahraje všechny úlohy z jednoho ročníku, které tu ještě nejsou' : 'Je nutné být přihlášený a na stránce v KSP template.'}>Nahrát
|
||||||
celý ročník</button>
|
celý ročník</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<button on:click={hideSelection}>Skrýt výběr</button>
|
||||||
|
<button on:click={showSelection}>Zobrazit výběr</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="taskDetails">
|
<div class="taskDetails">
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
import { createEdges } from "./tasks";
|
import { createEdges } from "./tasks";
|
||||||
import { taskStatuses } from "./task-status-cache";
|
import { taskStatuses } from "./task-status-cache";
|
||||||
import { grabAssignment } from "./ksp-task-grabber";
|
import { grabAssignment } from "./ksp-task-grabber";
|
||||||
|
import TaskDetailEditor from "./TaskDetailEditor.svelte";
|
||||||
|
|
||||||
export let tasks: TasksFile;
|
export let tasks: TasksFile;
|
||||||
export let nodeDraggingEnabled: boolean = false;
|
export let nodeDraggingEnabled: boolean = false;
|
||||||
|
@ -14,6 +15,7 @@ import { grabAssignment } from "./ksp-task-grabber";
|
||||||
export let showHiddenEdges: boolean = false;
|
export let showHiddenEdges: boolean = false;
|
||||||
export let selection: Set<TaskDescriptor> = new Set();
|
export let selection: Set<TaskDescriptor> = new Set();
|
||||||
export let showCenterMarker: boolean = false;
|
export let showCenterMarker: boolean = false;
|
||||||
|
export let showHidden: boolean = false;
|
||||||
|
|
||||||
let hoveredTask: null | TaskDescriptor = null;
|
let hoveredTask: null | TaskDescriptor = null;
|
||||||
|
|
||||||
|
@ -185,6 +187,10 @@ import { grabAssignment } from "./ksp-task-grabber";
|
||||||
window.addEventListener("mouseup", dragStop, { once: true });
|
window.addEventListener("mouseup", dragStop, { once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCurrentSelection(): Set<TaskDescriptor> {
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
setupZoom();
|
setupZoom();
|
||||||
});
|
});
|
||||||
|
@ -308,10 +314,13 @@ import { grabAssignment } from "./ksp-task-grabber";
|
||||||
stroke-dasharray="15,15" />
|
stroke-dasharray="15,15" />
|
||||||
{/if}
|
{/if}
|
||||||
{#each edges as edge}
|
{#each edges as edge}
|
||||||
<GraphEdge {edge} showLabelEdge={showHiddenEdges} />
|
{#if showHidden || !(edge?.dependee?.hidden || edge?.dependency?.hidden)}
|
||||||
|
<GraphEdge {edge} showLabelEdge={showHiddenEdges} />
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{#each nodes as task}
|
{#each nodes as task}
|
||||||
<GraphNode
|
{#if showHidden || !(task.hidden ?? false) }
|
||||||
|
<GraphNode
|
||||||
{task}
|
{task}
|
||||||
on:mousedown={dragStart}
|
on:mousedown={dragStart}
|
||||||
selected={selectionToolEnabled && selection.has(task)}
|
selected={selectionToolEnabled && selection.has(task)}
|
||||||
|
@ -320,6 +329,7 @@ import { grabAssignment } from "./ksp-task-grabber";
|
||||||
on:hoveringChange={nodeHover(task)}
|
on:hoveringChange={nodeHover(task)}
|
||||||
status={$taskStatuses.get(task.id)}
|
status={$taskStatuses.get(task.id)}
|
||||||
on:dblclick={nodeDoubleClick(task)} />
|
on:dblclick={nodeDoubleClick(task)} />
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{#if hoveredTask != null && hoveredTask.type == "open-data" }
|
{#if hoveredTask != null && hoveredTask.type == "open-data" }
|
||||||
<g class="tooltip">
|
<g class="tooltip">
|
||||||
|
|
|
@ -1,15 +1,29 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { TaskEdge } from "./graph";
|
import type { TaskEdge } from "./graph";
|
||||||
|
|
||||||
export let edge: TaskEdge;
|
export let edge: TaskEdge;
|
||||||
export let showLabelEdge: boolean = false;
|
export let showLabelEdge: boolean = false;
|
||||||
|
|
||||||
$: [x1, y1] = edge?.dependency?.position ?? [0,0];
|
$: [x1, y1] = edge?.dependency?.position ?? [0, 0];
|
||||||
$: [x2, y2] = edge?.dependee?.position ?? [0, 0];
|
$: [x2, y2] = edge?.dependee?.position ?? [0, 0];
|
||||||
$: dx = x1 - x2
|
$: dx = x1 - x2;
|
||||||
$: dy = y1 - y2
|
$: dy = y1 - y2;
|
||||||
|
|
||||||
|
$: hidden =
|
||||||
|
(edge?.dependee?.hidden ?? false) ||
|
||||||
|
(edge?.dependency?.hidden ?? false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if showLabelEdge || (edge?.dependee?.type ?? null) != "label"}
|
<style>
|
||||||
<path d="m {x2} {y2+0} c 0 0 {dx} {dy-40} {dx} {dy-20}" style="fill:none; stroke: #aaa; stroke-width: 3px" />
|
.hidden {
|
||||||
|
stroke-opacity: 0.5;
|
||||||
|
fill-opacity: 0.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{#if showLabelEdge || (edge?.dependee?.type ?? null) != 'label'}
|
||||||
|
<path
|
||||||
|
d="m {x2} {y2 + 0} c 0 0 {dx} {dy - 40} {dx} {dy - 20}"
|
||||||
|
style="fill:none; stroke: #aaa; stroke-width: 3px"
|
||||||
|
class={hidden ? 'hidden' : ''} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -41,8 +41,7 @@
|
||||||
// every time after that
|
// every time after that
|
||||||
$: {
|
$: {
|
||||||
task.title;
|
task.title;
|
||||||
if (text_element)
|
if (text_element) ensureTextFits();
|
||||||
ensureTextFits();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function dblclick(e: MouseEvent) {
|
function dblclick(e: MouseEvent) {
|
||||||
|
@ -56,6 +55,12 @@
|
||||||
g:not(.label) {
|
g:not(.label) {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
fill-opacity: 0.5;
|
||||||
|
stroke-opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
fill: gainsboro;
|
fill: gainsboro;
|
||||||
|
@ -81,7 +86,8 @@
|
||||||
fill: green; /* TODO */
|
fill: green; /* TODO */
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected > ellipse, .selected > rect {
|
.selected > ellipse,
|
||||||
|
.selected > rect {
|
||||||
stroke-width: 4px;
|
stroke-width: 4px;
|
||||||
stroke: red;
|
stroke: red;
|
||||||
}
|
}
|
||||||
|
@ -94,9 +100,12 @@
|
||||||
on:mouseleave={leave}
|
on:mouseleave={leave}
|
||||||
on:click={click}
|
on:click={click}
|
||||||
on:dblclick={dblclick}
|
on:dblclick={dblclick}
|
||||||
class="{status == null ? '' : status.solved ? 'solved' : status.submitted ? 'submitted' : ''} {task.type} {selected ? 'selected' : 'notSelected'}">
|
class="{status == null ? '' : status.solved ? 'solved' : status.submitted ? 'submitted' : ''}
|
||||||
|
{task.type}
|
||||||
|
{selected ? 'selected' : 'notSelected'}
|
||||||
|
{task.hidden ?? false ? 'hidden' : ''}">
|
||||||
{#if task.type == 'label'}
|
{#if task.type == 'label'}
|
||||||
{#if selected }
|
{#if selected}
|
||||||
<ellipse rx={ellipse_rx} ry={20} {cx} {cy} />
|
<ellipse rx={ellipse_rx} ry={20} {cx} {cy} />
|
||||||
{/if}
|
{/if}
|
||||||
<text
|
<text
|
||||||
|
@ -109,8 +118,14 @@
|
||||||
{task.title == null ? task.id : task.title}
|
{task.title == null ? task.id : task.title}
|
||||||
</text>
|
</text>
|
||||||
{:else}
|
{:else}
|
||||||
{#if task.type == "text"}
|
{#if task.type == 'text'}
|
||||||
<rect class="taskNode" x={cx-ellipse_rx} y={cy-20} height={40} width={2*ellipse_rx} rx={3} />
|
<rect
|
||||||
|
class="taskNode"
|
||||||
|
x={cx - ellipse_rx}
|
||||||
|
y={cy - 20}
|
||||||
|
height={40}
|
||||||
|
width={2 * ellipse_rx}
|
||||||
|
rx={3} />
|
||||||
{:else}
|
{:else}
|
||||||
<ellipse class="taskNode" rx={ellipse_rx} ry={20} {cx} {cy} />
|
<ellipse class="taskNode" rx={ellipse_rx} ry={20} {cx} {cy} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -6,6 +6,7 @@ export type TaskDescriptor = {
|
||||||
requires: string[]
|
requires: string[]
|
||||||
comment?: string
|
comment?: string
|
||||||
position?: [number, number]
|
position?: [number, number]
|
||||||
|
hidden?: boolean
|
||||||
} & (
|
} & (
|
||||||
{
|
{
|
||||||
type: "open-data",
|
type: "open-data",
|
||||||
|
|
426
tasks.json
426
tasks.json
File diff suppressed because it is too large
Load diff
Reference in a new issue