Spaces:
Running
Running
File size: 5,450 Bytes
9b3539b 7fcb083 1dc03e3 9b3539b 1dc03e3 9b3539b 3010d5b af53b62 9b3539b 3010d5b 9b3539b 7fcb083 1dc03e3 4767f72 7fcb083 3010d5b 7fcb083 1dc03e3 3010d5b 801415b 3010d5b 4767f72 f24a87d 05b0e84 9b3539b 1dc03e3 3010d5b 4767f72 3833905 9b3539b 4767f72 ca01fa3 c1a1d02 4767f72 f24a87d 05b0e84 f24a87d 3833905 05b0e84 9b3539b 1dc03e3 9b3539b ca01fa3 3833905 69ec235 ca01fa3 9b3539b fc7156e 1dc03e3 9b3539b 1dc03e3 9b3539b 1dc03e3 a18645a 3010d5b 9b3539b 32d552c 9b3539b 3833905 767e953 3833905 767e953 3833905 767e953 3833905 767e953 3833905 4133481 3833905 1dc03e3 9b3539b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
<script lang="ts">
import { getContext } from 'svelte';
import { Handle, useSvelteFlow, useUpdateNodeInternals, type NodeProps, NodeResizeControl } from '@xyflow/svelte';
import ChevronDownRight from 'virtual:icons/tabler/chevron-down-right';
const { updateNodeData } = useSvelteFlow();
const updateNodeInternals = useUpdateNodeInternals();
type $$Props = NodeProps;
export let nodeStyle = '';
export let containerStyle = '';
export let id: $$Props['id']; id;
export let data: $$Props['data'];
export let dragHandle: $$Props['dragHandle'] = undefined; dragHandle;
export let type: $$Props['type'] = undefined; type;
export let selected: $$Props['selected'] = undefined; selected;
export let isConnectable: $$Props['isConnectable'] = undefined; isConnectable;
export let zIndex: $$Props['zIndex'] = undefined; zIndex;
export let width: $$Props['width'] = undefined; width;
export let height: $$Props['height'] = undefined; height;
export let dragging: $$Props['dragging']; dragging;
export let targetPosition: $$Props['targetPosition'] = undefined; targetPosition;
export let sourcePosition: $$Props['sourcePosition'] = undefined; sourcePosition;
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = undefined; positionAbsoluteX;
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = undefined; positionAbsoluteY;
export let onToggle = () => {};
$: store = getContext('LynxKite store');
$: expanded = !data.collapsed;
function titleClicked() {
const i = $store.workspace.nodes.findIndex((n) => n.id === id);
$store.workspace.nodes[i].data.collapsed = expanded;
onToggle({ expanded });
// Trigger update.
data = data;
updateNodeInternals();
}
function asPx(n: number | undefined) {
return n ? n + 'px' : undefined;
}
function getHandles(inputs, outputs) {
const handles: {
position: 'top' | 'bottom' | 'left' | 'right',
name: string,
index: number,
offsetPercentage: number,
showLabel: boolean,
}[] = [];
for (const e of Object.values(inputs)) {
handles.push({ ...e, type: 'target' });
}
for (const e of Object.values(outputs)) {
handles.push({ ...e, type: 'source' });
}
const counts = { top: 0, bottom: 0, left: 0, right: 0 };
for (const e of handles) {
e.index = counts[e.position];
counts[e.position]++;
}
for (const e of handles) {
e.offsetPercentage = 100 * (e.index + 1) / (counts[e.position] + 1);
const simpleHorizontal = counts.top === 0 && counts.bottom === 0 && handles.length <= 2;
const simpleVertical = counts.left === 0 && counts.right === 0 && handles.length <= 2;
e.showLabel = !simpleHorizontal && !simpleVertical;
}
return handles;
}
$: handles = getHandles(data.meta?.inputs || {}, data.meta?.outputs || {});
const handleOffsetDirection = { top: 'left', bottom: 'left', left: 'top', right: 'top' };
</script>
<div class="node-container" class:expanded={expanded}
style:width={asPx(width)} style:height={asPx(expanded ? height : undefined)} style={containerStyle}>
<div class="lynxkite-node" style={nodeStyle}>
<div class="title" on:click={titleClicked}>
{data.title}
{#if data.error}<span class="title-icon">⚠️</span>{/if}
{#if !expanded}<span class="title-icon">⋯</span>{/if}
</div>
{#if expanded}
{#if data.error}
<div class="error">{data.error}</div>
{/if}
<slot />
{/if}
{#each handles as handle}
<Handle
id={handle.name} type={handle.type} position={handle.position}
style="{handleOffsetDirection[handle.position]}: {handle.offsetPercentage}%">
{#if handle.showLabel}<span class="handle-name">{handle.name.replace(/_/g, " ")}</span>{/if}
</Handle>
{/each}
</div>
{#if expanded}
<NodeResizeControl
minWidth={100}
minHeight={50}
style="background: transparent; border: none;"
onResizeStart={() => updateNodeData(id, { beingResized: true })}
onResizeEnd={() => updateNodeData(id, { beingResized: false })}
>
<ChevronDownRight class="node-resizer" />
</NodeResizeControl>
{/if}
</div>
<style>
.error {
background: #ffdddd;
padding: 8px;
font-size: 12px;
}
.title-icon {
margin-left: 5px;
float: right;
}
.node-container {
padding: 8px;
position: relative;
}
.lynxkite-node {
box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.3);
border-radius: 4px;
background: white;
}
.expanded .lynxkite-node {
overflow-y: auto;
height: 100%;
}
.title {
background: oklch(75% 0.2 55);
font-weight: bold;
padding: 8px;
}
.handle-name {
font-size: 10px;
color: black;
letter-spacing: 0.05em;
text-align: right;
white-space: nowrap;
position: absolute;
top: -5px;
backdrop-filter: blur(10px);
padding: 2px 8px;
border-radius: 4px;
visibility: hidden;
}
:global(.left) .handle-name {
right: 20px;
}
:global(.right) .handle-name {
left: 20px;
}
:global(.top) .handle-name,
:global(.bottom) .handle-name {
top: -5px;
left: 5px;
backdrop-filter: none;
}
.node-container:hover .handle-name {
visibility: visible;
}
:global(.node-resizer) {
position: absolute;
bottom: 8px;
right: 8px;
cursor: nwse-resize;
color: var(--bs-border-color);
}
</style>
|