163 lines
4.3 KiB
Svelte
163 lines
4.3 KiB
Svelte
<script lang="ts">
|
|
import { put } from '$lib/utils';
|
|
import IconPicker from './IconPicker.svelte';
|
|
import Rect from './Rect.svelte';
|
|
import type { Node, ActionType, LinkNode } from './types';
|
|
|
|
//
|
|
// consts
|
|
//
|
|
const connectionPointSize = 10;
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
let hover = $state(false);
|
|
let iconSelector: HTMLDialogElement;
|
|
|
|
let {
|
|
node = $bindable(),
|
|
grid_size,
|
|
canvasX,
|
|
canvasY,
|
|
mouseAction,
|
|
onremove,
|
|
linkMode,
|
|
onNodePointClick,
|
|
linkSource
|
|
}: {
|
|
node: Node;
|
|
grid_size: number;
|
|
canvasX: (x: number) => number;
|
|
canvasY: (y: number) => number;
|
|
mouseAction: ActionType;
|
|
onremove: () => void;
|
|
linkMode: boolean;
|
|
onNodePointClick: (x: number, y: number) => void;
|
|
linkSource: LinkNode | undefined;
|
|
} = $props();
|
|
|
|
let cx = $derived(canvasX(node.x));
|
|
let cy = $derived(canvasY(node.y));
|
|
|
|
async function save() {
|
|
try {
|
|
const r = await put('user/status/node', node);
|
|
node = r;
|
|
} catch (e) {
|
|
console.log('TODO inform the user', e);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<Rect x={cx - 1} y={cy - 1} height={node.height * grid_size + 1} width={node.width * grid_size + 1}>
|
|
<div
|
|
class="bg-white shadow-lg border-slate-300 border-solid border rounded-lg w-full h-full flex flex-col justify-center items-center gap-2 relative"
|
|
role="none"
|
|
onmouseenter={() => {
|
|
if (mouseAction === undefined || mouseAction === 'link') {
|
|
hover = true;
|
|
}
|
|
}}
|
|
onmouseleave={() => {
|
|
if (hover) {
|
|
hover = false;
|
|
}
|
|
}}
|
|
>
|
|
<button
|
|
disabled={node.permission !== 0}
|
|
onclick={() => {
|
|
iconSelector.showModal();
|
|
}}
|
|
>
|
|
<h1>
|
|
{#if node.icon}
|
|
<span class="bi bi-{node.icon}"></span>
|
|
{:else}
|
|
Select an icon
|
|
{/if}
|
|
</h1>
|
|
</button>
|
|
<div class="p-2">
|
|
{#if node.permission === 0}
|
|
<input bind:value={node.name} class="finput text-center" onblur={save} />
|
|
{:else}
|
|
{node.name}
|
|
{/if}
|
|
</div>
|
|
{#if node.permission === 0 && !linkMode}
|
|
<button
|
|
class="bg-red-400 hover:bg-red-800 -top-[10px] -right-[10px] rounded-full h-[20px] w-[20px] absolute text-center text-white leading-none"
|
|
onclick={onremove}
|
|
>
|
|
<div class="bi bi-x mt-[2px]"></div>
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
|
|
<!--
|
|
Node connections ponints
|
|
-->
|
|
{#if hover && linkMode}
|
|
<!-- Top -->
|
|
{#each { length: node.width } as _, i}
|
|
{@const adjust = grid_size / 2 + grid_size * i - connectionPointSize / 2}
|
|
<button
|
|
class="{linkSource?.x === i + 1 && linkSource?.y === 0
|
|
? 'bg-red-200 hover:bg-red-800'
|
|
: 'bg-blue-200 hover:bg-blue-800'} rounded-full absolute"
|
|
style="top: 0px; left: {adjust}px; width: {connectionPointSize}px; height: {connectionPointSize}px;"
|
|
onclick={() => onNodePointClick(i, -1)}
|
|
></button>
|
|
{/each}
|
|
|
|
<!-- Bottom -->
|
|
{#each { length: node.width } as _, i}
|
|
{@const adjust = grid_size / 2 + grid_size * i - connectionPointSize / 2}
|
|
<button
|
|
class="{linkSource?.x === i + 1 && linkSource?.y === node.height
|
|
? 'bg-red-200 hover:bg-red-800'
|
|
: 'bg-blue-200 hover:bg-blue-800'} rounded-full absolute"
|
|
style="bottom: 0px; left: {adjust}px; width: {connectionPointSize}px; height: {connectionPointSize}px;"
|
|
onclick={() => onNodePointClick(i, node.height)}
|
|
></button>
|
|
{/each}
|
|
|
|
<!-- Left -->
|
|
{#each { length: node.height } as _, i}
|
|
{@const adjust = grid_size / 2 + grid_size * i - connectionPointSize / 2}
|
|
<button
|
|
class="{linkSource?.x === 0 && linkSource?.y === i + 1
|
|
? 'bg-red-200 hover:bg-red-800'
|
|
: 'bg-blue-200 hover:bg-blue-800'} rounded-full absolute"
|
|
style="left: 0px; top: {adjust}px; width: {connectionPointSize}px; height: {connectionPointSize}px;"
|
|
onclick={() => onNodePointClick(-1, i)}
|
|
></button>
|
|
{/each}
|
|
|
|
<!-- Right -->
|
|
{#each { length: node.height } as _, i}
|
|
{@const adjust = grid_size / 2 + grid_size * i - connectionPointSize / 2}
|
|
<button
|
|
class="{linkSource?.x === node.width && linkSource?.y === i + 1
|
|
? 'bg-red-200 hover:bg-red-800'
|
|
: 'bg-blue-200 hover:bg-blue-800'} rounded-full absolute"
|
|
style="right: 0px; top: {adjust}px; width: {connectionPointSize}px; height: {connectionPointSize}px;"
|
|
onclick={() => onNodePointClick(node.width, i)}
|
|
></button>
|
|
{/each}
|
|
{/if}
|
|
</Rect>
|
|
|
|
<IconPicker
|
|
bind:dialog={iconSelector}
|
|
onselect={(text) => {
|
|
node.icon = text;
|
|
window.requestAnimationFrame(() => {
|
|
save();
|
|
});
|
|
}}
|
|
/>
|