Skip to content

Commit

Permalink
Merge pull request #286 from open-source-labs/dev
Browse files Browse the repository at this point in the history
v7.0.26
  • Loading branch information
briangregoryholmes authored Apr 25, 2023
2 parents 41c6599 + f1c62b2 commit 1feed8f
Show file tree
Hide file tree
Showing 17 changed files with 320 additions and 80 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "svelvet",
"version": "7.0.25",
"version": "7.0.26",
"description": "A lightweight Svelte component library for building dynamic, node-based user interfaces",
"keywords": [
"svelte",
Expand Down
49 changes: 29 additions & 20 deletions src/example-components/InputNode.svelte
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
<script lang="ts">
import { Anchor, Node } from '$lib';
let title = '';
let description = '';
let text = 'Test';
let checked = true;
let color = '#ff0000';
let date = new Date();
let datetime = new Date();
let email = '';
let month = '';
let number = 0;
let password = '';
let radio = '';
let range = 0;
let search = '';
let tel = '';
let url = '';
let week = '';
</script>

<Node bgColor="black" label="StartNode" borderRadius={10}>
<div class="node">
<input
class="input-pointer"
bind:value={title}
title="Story Title"
placeholder="Story Title"
type="text"
/>
<input
class="input-pointer"
bind:value={description}
title="Story Description"
placeholder="Story Description"
type="text"
/>
<input class="checkbox" type="checkbox" />

<button on:click={() => alert('Test')}>Test</button>
<Anchor output direction="east" />
<input type="text" bind:value={text} />
<input type="checkbox" bind:checked />
<input type="color" bind:value={color} />
<input type="date" bind:value={date} />
<input type="datetime-local" bind:value={datetime} />
<input type="email" bind:value={email} />
<input type="month" bind:value={month} />
<input type="number" bind:value={number} />
<input type="password" bind:value={password} />
<input type="radio" bind:value={radio} />
<input type="range" bind:value={range} />
<input type="search" bind:value={search} />
<input type="tel" bind:value={tel} />
<button>Test</button>
</div>
</Node>

Expand Down
70 changes: 49 additions & 21 deletions src/lib/components/Anchor/Anchor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@
export let locked = false;
export let edgeStyle: EdgeStyle | null = null;
const mounted = getContext<Writable<number | true>>('mounted');
let animationFrameId: number;
let anchorElement: HTMLDivElement;
let anchor: Anchor;
let tracking = false;
let hovering = false;
let previousConnectionCount = 0;
let type: InputType = input === output ? null : input ? 'input' : 'output';
let assignedConnections: Connections = [];
const nodeEdge = node.edge;
const anchors = node.anchors;
Expand All @@ -77,7 +80,6 @@
const linkingOutput = graph.linkingOutput;
const linkingAny = graph.linkingAny;
$: isWaitingForConnection = connections.length > 0 || $nodeLevelConnections?.length > 0;
$: connecting =
input === output
? $linkingAny === anchor
Expand Down Expand Up @@ -109,12 +111,16 @@
anchors.add(anchor, anchor.id);
checkConnections();
if (!input) {
const poppedConnections = $nodeLevelConnections?.pop();
if (poppedConnections) assignedConnections = poppedConnections;
}
// This is to avoid a strange bug where the anchor positions are off
// It only seems to happen in dev and doesn't seem to affect the final product
setTimeout(() => {
updatePosition();
}, 20);
}, 0);
});
beforeUpdate(() => {
Expand All @@ -127,10 +133,18 @@
cancelAnimationFrame(animationFrameId);
});
// If the user has specifcied connections, we check if the anchor pair is in memory yet
$: if (isWaitingForConnection) {
$anchors;
checkConnections();
$: if ($mounted === graph.nodes.count() && connections.length) {
checkDirectConnections();
}
// // If the user has specifcied connections, we check once all nodes have mounted
$: if ($mounted === graph.nodes.count() && assignedConnections.length) {
checkNodeLevelConnections();
}
$: {
$connectedAnchors;
updatePosition();
}
// If an anchor is added to the store, we update all anchor positions
Expand Down Expand Up @@ -203,11 +217,12 @@
}
}
function createCursorEdge(source: Anchor | null, target: Anchor | null) {
function createCursorEdge(source: Anchor | null, target: Anchor | null, disconnect = false) {
const edgeConfig: EdgeConfig = {
color: edgeColor,
label: { text: edgeLabel }
};
if (disconnect) edgeConfig.disconnect = true;
if (edgeStyle) edgeConfig.type = edgeStyle;
// Create a temporary edge to track the cursor
const newEdge = createEdge({ source, target }, source?.edge || null, edgeConfig);
Expand Down Expand Up @@ -406,29 +421,35 @@
destroy();
if (source.type === 'output') {
createCursorEdge(source, null);
createCursorEdge(source, null, true);
disconnectStore();
const store: ReturnType<typeof generateOutput> = source.store as ReturnType<
typeof generateOutput
>;
$linkingOutput = { anchor: source, store };
} else {
createCursorEdge(source, null);
createCursorEdge(source, null, true);
$linkingAny = source;
}
}
function checkConnections() {
function checkNodeLevelConnections() {
assignedConnections.forEach((connection, index) => {
if (!connection) return;
const connected = processConnection(connection);
if (connected) connections[index] = null;
});
assignedConnections = assignedConnections.filter((connection) => connection !== null);
}
function checkDirectConnections() {
connections.forEach((connection, index) => {
if (!connection) return;
const connected = processConnection(connection);
if (connected) connections.splice(index, 1);
if (connected) connections[index] = null;
});
if (!input) {
$nodeLevelConnections.forEach((connection, index) => {
const connected = processConnection(connection);
if (connected) $nodeLevelConnections.splice(index, 1);
});
}
connections = connections.filter((connection) => connection !== null);
}
const processConnection = (connection: [string | number, string | number] | string | number) => {
Expand All @@ -448,14 +469,18 @@
const nodekey: NodeKey = `N-${nodeId}`;
// Look up node in store
const nodeToConnect = graph.nodes.get(nodekey);
if (!nodeToConnect) return false;
if (!nodeToConnect) {
return false;
}
if (!anchorId) {
// Connect to the anchor with the fewest connections
const anchorStore = get(nodeToConnect.anchors);
const anchors = Object.values(anchorStore);
if (!anchors.length) return false;
if (!anchors.length) {
return false;
}
anchorToConnect = anchors.reduce<Anchor | null>((a, b) => {
if (!a && b.type === 'output') return null;
if (b.type === 'output') return a;
Expand All @@ -470,7 +495,10 @@
anchorToConnect = nodeToConnect.anchors.get(anchorKey) || null;
}
if (!anchorToConnect) return false;
if (!anchorToConnect) {
return false;
}
connectAnchors(anchor, anchorToConnect);
if (anchorToConnect.store && (inputsStore || outputStore)) {
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/Edge/Edge.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@
/>
<slot {path} {destroy}>
<path
class="edge"
id={edgeKey}
class:animate
d={path}
Expand Down
38 changes: 30 additions & 8 deletions src/lib/components/Node/InternalNode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -140,23 +140,36 @@
function handleNodeClicked(e: MouseEvent) {
const targetElement = e.target as HTMLElement; // Cast e.target to HTMLElement
if (targetElement.tagName !== 'INPUT') {
e.preventDefault();
}
// Bring node to front regardless of event target
if ($zIndex !== $maxZIndex && $zIndex !== Infinity) $zIndex = ++$maxZIndex;
// If the event target is an input, don't do anything
if (targetElement.tagName === 'INPUT') return;
//Dispatch nodeClick event fo rdeveloper use
dispatch('nodeClicked', { node, e });
// Stop event from propagating to other mousedown listeners
e.stopPropagation();
// Prevent the default behavir of text selection on drag
// May be safe to move this to the global mouse move listener
e.preventDefault();
// If the node or graph is locked, don't do anything
if ($locked || $nodeLock) return;
if ($zIndex !== $maxZIndex && $zIndex !== Infinity) $zIndex = ++$maxZIndex;
dispatch('nodeClicked', { node, e });
// Set our global tracking boolean to true
$tracking = true;
const { button } = e;
// Capture the initial click position
$initialClickPosition = $cursor;
// Right click sets editing node
if (button === 2 && $editable) {
if (e.button === 2 && $editable) {
$editing = node;
}
// Handle selection logic
nodeSelectLogic();
}
Expand Down Expand Up @@ -203,6 +216,13 @@
function destroy() {
nodeStore.delete(id);
}
function onMouseUp(e: MouseEvent) {
const mouseDeltaX = $cursor.x - $initialClickPosition.x;
const mouseDeltaY = $cursor.y - $initialClickPosition.y;
const combinedDelta = Math.abs(mouseDeltaX) + Math.abs(mouseDeltaY);
if (combinedDelta < 4) dispatch('nodeReleased', { e });
}
</script>

<!-- svelte-ignore a11y-non-interactive-element -->
Expand All @@ -226,6 +246,7 @@
style:transform="rotate({$rotation}deg)"
on:contextmenu|preventDefault|stopPropagation
on:keydown|preventDefault|self={handleKeydown}
on:mouseup={onMouseUp}
bind:this={DOMnode}
use:grabHandle
tabIndex={0}
Expand All @@ -252,6 +273,7 @@
cursor: not-allowed;
}
.selected {
box-shadow: 0 0 0 var(--border-width) var(--selection-color);
box-shadow: 0 0 0 var(--border-width) var(--selection-color),
0 0 0 var(--border-width) var(--border-color), var(--shadow-elevation-medium);
}
</style>
18 changes: 17 additions & 1 deletion src/lib/components/Node/Node.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@
zIndex,
direction,
locked,
connections,
rotation
};
if (connections.length && outputs) config.connections = processConnections(connections);
if (borderWidth) config.borderWidth = borderWidth;
if (borderRadius) config.borderRadius = borderRadius;
if (borderColor) config.borderColor = borderColor;
Expand All @@ -103,6 +103,21 @@
graph.nodes.add(node, node.id);
});
function processConnections(connectionsArray: Connections) {
const processedConnections: Array<Connections> = Array(outputs)
.fill(null)
.map(() => []);
let currentAnchor = 0;
connectionsArray.forEach((connection) => {
currentAnchor = currentAnchor % outputs;
processedConnections[currentAnchor].push(connection);
currentAnchor++;
});
return processedConnections.reverse();
}
// All these functions below essentially enable data binding to the props
// We should not recommend developers utilize this, but it is a nice feature
$: if (node) {
Expand Down Expand Up @@ -158,6 +173,7 @@
let:grabHandle
on:nodeClicked
on:nodeMount
on:nodeReleased
{node}
>
<slot {selected} {grabHandle} {node} {destroy}>
Expand Down
Loading

0 comments on commit 1feed8f

Please sign in to comment.