feat(svelte): custom nodes

This commit is contained in:
moklick
2023-02-21 14:36:07 +01:00
parent 6b192d90d5
commit aea43103af
12 changed files with 199 additions and 90 deletions
+1
View File
@@ -10,6 +10,7 @@
#root {
height: 100%;
margin: 0;
font-family: Arial, Helvetica, sans-serif;
}
</style>
%sveltekit.head%
@@ -0,0 +1,22 @@
<script lang="ts">
import Handle from '$lib/components/Handle/index.svelte';
import { Position } from '@reactflow/system';
export let data: { label: string } = { label: 'Node' };
export let xPos: number = 0;
export let yPos: number = 0;
</script>
<div class="custom">
<div>{data.label}</div>
<div>{~~xPos}, {~~yPos}</div>
<Handle type="target" position={Position.Left} />
<Handle type="source" position={Position.Right} />
</div>
<style>
.custom {
background: #ffcc00;
padding: 10px;
}
</style>
@@ -6,13 +6,14 @@
export let type: HandleType = 'source';
export let position: Position = Position.Top;
export let id: string | null = null;
export let className: string | null = null;;
let className: string | null = null;
export { className as class };
const isTarget = type === 'target';
const nodeId = getContext('rf_nodeid');
</script>
<div
data-handleid={id}
data-nodeid={nodeId}
@@ -28,7 +29,7 @@
class:source={!isTarget}
class:target={isTarget}
>
<slot></slot>
<slot />
</div>
@@ -46,27 +47,26 @@
}
.bottom {
top: auto;
top: 100%;
left: 50%;
bottom: -4px;
transform: translate(-50%, 0);
transform: translate(-50%, -50%);
}
.top {
left: 50%;
top: -4px;
transform: translate(-50%, 0);
top: 0;
transform: translate(-50%, -50%);
}
.left {
top: 50%;
left: -4px;
transform: translate(0, -50%);
left: 0;
transform: translate(-50%, -50%);
}
.right {
right: -4px;
top: 50%;
transform: translate(0, -50%);
left: 100%;
transform: translate(-50%, -50%);
}
</style>
@@ -1,24 +1,30 @@
<script lang="ts">
import { onMount, setContext } from 'svelte';
import type { XYPosition } from '@reactflow/system';
import { onMount, setContext, SvelteComponentTyped, type ComponentType } from 'svelte';
import { type XYPosition, Position } from '@reactflow/system';
import drag from '$lib/actions/drag'
import { useStore } from '$lib/store';
import DefaultNode from './DefaultNode.svelte';
import type { NodeProps } from '$lib/types';
export let id: string;
export let id: NodeProps['id'];
export let data: NodeProps['data'] = {};
export let selected: NodeProps['selected'] = false;
export let positionAbsolute: XYPosition = { x: 0, y: 0 };
export let position: XYPosition = { x: 0, y: 0 };
export let dragging: boolean = false;
export let data: any = {};
export let resizeObserver: ResizeObserver | null = null;
export let style: any = {};
export let width: number = 0;
export let height: number = 0;
export let type: string = 'default';
export let sourcePosition: Position = Position.Bottom;
export let targetPosition: Position = Position.Top;
let nodeRef: HTMLDivElement;
const { nodesStore, transformStore, updateNodePositions } = useStore();
const { nodesStore, transformStore, updateNodePositions, nodeTypesStore } = useStore();
const nodeComponent: typeof SvelteComponentTyped<NodeProps> = $nodeTypesStore[type] || DefaultNode;
setContext('rf_nodeid', id);
@@ -37,17 +43,26 @@
class:initializing={!width && !height}
class:dragging={dragging}
bind:this={nodeRef}
style="transform: translate({positionAbsolute.x}px, {positionAbsolute.y}px);"
style:transform={`translate(${positionAbsolute.x}px, ${positionAbsolute.y}px)`}
{style}
data-id={id}
>
<svelte:component this={DefaultNode} data={data} />
<svelte:component
this={nodeComponent}
{data}
{id}
{selected}
{sourcePosition}
{targetPosition}
isConnectable={true}
xPos={positionAbsolute.x}
yPos={positionAbsolute.y}
/>
</div>
<style>
.react-flow__node {
padding: 10px;
border-radius: 3px;
width: 50px;
font-size: 12px;
color: #222;
text-align: center;
@@ -1,12 +1,19 @@
<script lang="ts">
import { Position } from '@reactflow/system';
import Handle from '$lib/components/Handle.svelte';
import Handle from '$lib/components/Handle/index.svelte';
import type { NodeProps } from '$lib/types';
export let data: any = {};
export let isConnectable: boolean = true;
export let targetPosition: Position = Position.Top;
export let sourcePosition: Position = Position.Bottom;
interface $$Props extends NodeProps<{ label: string }>{}
export let id: $$Props['id'];
export let data: $$Props['data'] = { label: 'Node' };
export let isConnectable: $$Props['isConnectable'] = true;
export let targetPosition: $$Props['targetPosition'] = Position.Top;
export let sourcePosition: $$Props['sourcePosition'] = Position.Bottom;
export let xPos: $$Props['xPos'];
export let yPos: $$Props['yPos'];
export let selected: $$Props['selected'];
</script>
<Handle type="target" position={targetPosition} />
@@ -0,0 +1,13 @@
<script lang="ts">
import { Position } from '@reactflow/system';
import Handle from '$lib/components/Handle/index.svelte';
export let data: { label: string } = { label: 'Node' };
export let isConnectable: boolean = true;
export let sourcePosition: Position = Position.Bottom;
</script>
{data?.label}
<Handle type="source" position={sourcePosition} />
@@ -0,0 +1,13 @@
<script lang="ts">
import { Position } from '@reactflow/system';
import Handle from '$lib/components/Handle/index.svelte';
export let data: { label: string } = { label: 'Node' };
export let isConnectable: boolean = true;
export let targetPosition: Position = Position.Top;
</script>
{data?.label}
<Handle type="target" position={targetPosition} />
@@ -1,5 +1,5 @@
<script lang="ts">
import { setContext, onMount } from 'svelte'
import { setContext, onMount, SvelteComponent } from 'svelte'
import cc from 'classcat';
import type { Node, Edge } from '@reactflow/system';
@@ -17,6 +17,7 @@
export let nodes: Node[] = [];
export let edges: Edge[] = [];
export let fitView: boolean = false;
export let nodeTypes: Record<string, SvelteComponent>
let className: string = '';
export { className as class };
@@ -26,7 +27,8 @@
const store = createStore({
nodes,
edges,
fitView
fitView,
nodeTypes
});
setContext(key, {
+14
View File
@@ -24,6 +24,10 @@ import {
type EdgePosition
} from '$lib/container/EdgeRenderer/utils';
import { SelectionMode } from 'reactflow';
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
import InputNode from '$lib/components/nodes/InputNode.svelte';
import OutputNode from '$lib/components/nodes/OutputNode.svelte';
import type { NodeTypes } from '$lib/types';
export const key = Symbol();
@@ -33,6 +37,7 @@ type CreateStoreProps = {
fitView: boolean;
nodeOrigin?: NodeOrigin;
transform?: Transform;
nodeTypes?: NodeTypes;
id?: string;
};
@@ -59,6 +64,7 @@ type SvelteFlowStore = {
selectionRectModeStore: Writable<string | null>;
selectionMode: Writable<SelectionMode>;
selectionKeyPressedStore: Writable<boolean>;
nodeTypesStore: Writable<NodeTypes>;
zoomIn: (options?: ViewportHelperFunctionOptions) => void;
zoomOut: (options?: ViewportHelperFunctionOptions) => void;
fitView: (options?: ViewportHelperFunctionOptions) => boolean;
@@ -77,6 +83,7 @@ export function createStore({
transform = [0, 0, 1],
nodeOrigin = [0, 0],
fitView: fitViewOnInit = false,
nodeTypes = {},
id = '1'
}: CreateStoreProps): SvelteFlowStore {
const nodesStore = writable(nodes.map((n) => ({ ...n, positionAbsolute: n.position })));
@@ -94,6 +101,12 @@ export function createStore({
const selectionKeyPressedStore = writable(false);
const selectionRectModeStore = writable(null);
const selectionMode = writable(SelectionMode.Partial);
const nodeTypesStore = writable({
...nodeTypes,
input: nodeTypes.input || InputNode,
output: nodeTypes.output || OutputNode,
default: nodeTypes.default || DefaultNode
});
let fitViewOnInitDone = false;
@@ -279,6 +292,7 @@ export function createStore({
selectionKeyPressedStore,
selectionRectModeStore,
selectionMode,
nodeTypesStore,
updateNodePositions,
updateNodeDimensions,
zoomIn,
+15
View File
@@ -0,0 +1,15 @@
import type { SvelteComponentTyped } from 'svelte';
import type { Position } from '@reactflow/system';
export type NodeProps<NodeData extends Record<string, unknown> = Record<string, never>> = {
id: string;
data: NodeData;
xPos: number;
yPos: number;
selected: boolean;
isConnectable: boolean;
sourcePosition: Position;
targetPosition: Position;
};
export type NodeTypes = Record<string, typeof SvelteComponentTyped<NodeProps>>;
+8 -1
View File
@@ -2,12 +2,18 @@
import type { Node, Edge } from '@reactflow/system';
import SvelteFlow, { Controls, Background, BackgroundVariant, Minimap } from '../lib/index';
import CustomNode from '../customnodes/Custom.svelte';
const yNodes = 10;
const xNodes = 10;
const nodes: Node[] = [];
const edges: Edge[] = [];
const nodeTypes = {
custom: CustomNode,
};
let source = null;
for (let y = 0; y < yNodes; y++) {
@@ -19,6 +25,7 @@
id,
data,
position,
type: x === 0 ? 'custom' : 'default',
};
nodes.push(node);
@@ -57,7 +64,7 @@
// }]
</script>
<SvelteFlow {nodes} {edges} fitView >
<SvelteFlow {nodes} {edges} {nodeTypes} fitView>
<Controls />
<Background variant={BackgroundVariant.Dots} />
<Minimap />