Files
applications-tracker/site/src/routes/flow/NodeRect.svelte
2024-11-21 12:19:43 +00:00

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();
});
}}
/>