feat: node store interface
This commit is contained in:
81
app/src/lib/node-store/BreadCrumbs.svelte
Normal file
81
app/src/lib/node-store/BreadCrumbs.svelte
Normal file
@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
import type { Writable } from "svelte/store";
|
||||
|
||||
export let activeId: Writable<string>;
|
||||
$: [activeUser, activeCollection, activeNode] = $activeId.split(`/`);
|
||||
</script>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
{#if activeUser}
|
||||
<button
|
||||
on:click={() => {
|
||||
$activeId = "";
|
||||
}}
|
||||
>
|
||||
root
|
||||
</button>
|
||||
{#if activeCollection}
|
||||
<button
|
||||
on:click={() => {
|
||||
$activeId = activeUser;
|
||||
}}
|
||||
>
|
||||
{activeUser}
|
||||
</button>
|
||||
{#if activeNode}
|
||||
<button
|
||||
on:click={() => {
|
||||
$activeId = `${activeUser}/${activeCollection}`;
|
||||
}}
|
||||
>
|
||||
{activeCollection}
|
||||
</button>
|
||||
<span>{activeNode}</span>
|
||||
{:else}
|
||||
<span>{activeCollection}</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<span>{activeUser}</span>
|
||||
{/if}
|
||||
{:else}
|
||||
<span>root</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.breadcrumbs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.4em;
|
||||
gap: 0.8em;
|
||||
height: 1em;
|
||||
border-bottom: solid thin var(--outline);
|
||||
}
|
||||
.breadcrumbs > button {
|
||||
position: relative;
|
||||
background: none;
|
||||
font-family: var(--font-family);
|
||||
border: none;
|
||||
font-size: 1em;
|
||||
padding: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.breadcrumbs > button::after {
|
||||
content: "/";
|
||||
position: absolute;
|
||||
right: -11px;
|
||||
opacity: 0.5;
|
||||
white-space: pre;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.breadcrumbs > button:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.breadcrumbs > span {
|
||||
font-size: 1em;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
74
app/src/lib/node-store/DraggableNode.svelte
Normal file
74
app/src/lib/node-store/DraggableNode.svelte
Normal file
@ -0,0 +1,74 @@
|
||||
<script lang="ts">
|
||||
import NodeHtml from "$lib/graph-interface/node/NodeHTML.svelte";
|
||||
import type { NodeType } from "@nodes/types";
|
||||
|
||||
export let node: NodeType;
|
||||
|
||||
let dragging = false;
|
||||
|
||||
function handleDragStart(e: DragEvent) {
|
||||
dragging = true;
|
||||
const box = (e?.target as HTMLElement)?.getBoundingClientRect();
|
||||
if (e.dataTransfer === null) return;
|
||||
e.dataTransfer.effectAllowed = "move";
|
||||
e.dataTransfer.setData("data/node-id", node.id);
|
||||
e.dataTransfer.setData(
|
||||
"data/node-offset-x",
|
||||
Math.round(box.left - e.clientX).toString(),
|
||||
);
|
||||
e.dataTransfer.setData(
|
||||
"data/node-offset-y",
|
||||
Math.round(box.top - e.clientY).toString(),
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="node-wrapper" class:dragging>
|
||||
<div
|
||||
on:dragend={() => {
|
||||
dragging = false;
|
||||
}}
|
||||
draggable={true}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:dragstart={handleDragStart}
|
||||
>
|
||||
<NodeHtml
|
||||
inView={true}
|
||||
position={"relative"}
|
||||
z={5}
|
||||
node={{
|
||||
id: 0,
|
||||
type: node.id,
|
||||
position: [0, 0],
|
||||
tmp: {
|
||||
type: node,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.node-wrapper {
|
||||
width: fit-content;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
border: solid 2px transparent;
|
||||
padding: 5px;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.dragging {
|
||||
border: dashed 2px var(--outline);
|
||||
}
|
||||
.node-wrapper > div {
|
||||
opacity: 1;
|
||||
display: block;
|
||||
pointer-events: all;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.dragging > div {
|
||||
opacity: 0.2;
|
||||
}
|
||||
</style>
|
92
app/src/lib/node-store/NodeStore.svelte
Normal file
92
app/src/lib/node-store/NodeStore.svelte
Normal file
@ -0,0 +1,92 @@
|
||||
<script lang="ts">
|
||||
import type { GraphManager } from "$lib/graph-interface/graph-manager";
|
||||
import Node from "$lib/graph-interface/node/Node.svelte";
|
||||
import localStore from "$lib/helpers/localStore";
|
||||
import type { RemoteNodeRegistry } from "$lib/node-registry-client";
|
||||
import { Canvas } from "@threlte/core";
|
||||
import BreadCrumbs from "./BreadCrumbs.svelte";
|
||||
import NodeHtml from "$lib/graph-interface/node/NodeHTML.svelte";
|
||||
import DraggableNode from "./DraggableNode.svelte";
|
||||
|
||||
export let nodeRegistry: RemoteNodeRegistry;
|
||||
export let manager: GraphManager;
|
||||
|
||||
function handleImport() {
|
||||
nodeRegistry.load([$activeId]);
|
||||
}
|
||||
|
||||
const activeId = localStore<
|
||||
`${string}` | `${string}/${string}` | `${string}/${string}/${string}`
|
||||
>("nodes.store.activeId", "");
|
||||
|
||||
$: [activeUser, activeCollection, activeNode] = $activeId.split(`/`);
|
||||
</script>
|
||||
|
||||
<BreadCrumbs {activeId} />
|
||||
|
||||
<div class="wrapper">
|
||||
{#if !activeUser}
|
||||
<h3>Users</h3>
|
||||
{#await nodeRegistry.fetchUsers()}
|
||||
<div>Loading...</div>
|
||||
{:then users}
|
||||
{#each users as user}
|
||||
<button
|
||||
on:click={() => {
|
||||
$activeId = user.id;
|
||||
}}>{user.id}</button
|
||||
>
|
||||
{/each}
|
||||
{:catch error}
|
||||
<div>{error.message}</div>
|
||||
{/await}
|
||||
{:else if !activeCollection}
|
||||
{#await nodeRegistry.fetchUser(activeUser)}
|
||||
<div>Loading...</div>
|
||||
{:then user}
|
||||
<h3>Collections</h3>
|
||||
{#each user.collections as collection}
|
||||
<button
|
||||
on:click={() => {
|
||||
$activeId = collection.id;
|
||||
}}
|
||||
>
|
||||
{collection.id.split(`/`)[1]}
|
||||
</button>
|
||||
{/each}
|
||||
{:catch error}
|
||||
<div>{error.message}</div>
|
||||
{/await}
|
||||
{:else if !activeNode}
|
||||
<h3>Nodes</h3>
|
||||
{#await nodeRegistry.fetchCollection(`${activeUser}/${activeCollection}`)}
|
||||
<div>Loading...</div>
|
||||
{:then collection}
|
||||
{#each collection.nodes as node}
|
||||
<button
|
||||
on:click={() => {
|
||||
$activeId = node.id;
|
||||
}}
|
||||
>
|
||||
{node.id.split(`/`)[2]}
|
||||
</button>
|
||||
{/each}
|
||||
{:catch error}
|
||||
<div>{error.message}</div>
|
||||
{/await}
|
||||
{:else}
|
||||
{#await nodeRegistry.fetchNodeDefinition(`${activeUser}/${activeCollection}/${activeNode}`)}
|
||||
<div>Loading...</div>
|
||||
{:then node}
|
||||
<DraggableNode {node} />
|
||||
{:catch error}
|
||||
<div>{error.message}</div>
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
31
app/src/lib/node-store/Spinner.svelte
Normal file
31
app/src/lib/node-store/Spinner.svelte
Normal file
@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<span class="spinner"></span>
|
||||
|
||||
<style>
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.spinner::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
pointer-events: none;
|
||||
animation: spin 1s linear infinite;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' class='icon icon-tabler icons-tabler-outline icon-tabler-loader-2'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 3a9 9 0 1 0 9 9' /%3E%3C/svg%3E");
|
||||
background-size: cover;
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user