migrate all components
This commit is contained in:
@@ -50,7 +50,7 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@svelte-put/shortcut": "^3.1.0",
|
||||
"@svelte-put/shortcut": "^4.0.0",
|
||||
"@xyflow/system": "workspace:*",
|
||||
"classcat": "^5.0.5"
|
||||
},
|
||||
@@ -74,7 +74,7 @@
|
||||
"postcss-rename": "^0.6.1",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-svelte": "^3.2.8",
|
||||
"svelte": "^5.1.15",
|
||||
"svelte": "^5.1.16",
|
||||
"svelte-check": "^4.0.7",
|
||||
"svelte-eslint-parser": "^0.43.0",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
|
||||
@@ -1,90 +1,86 @@
|
||||
<svelte:options immutable />
|
||||
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, setContext } from 'svelte';
|
||||
import { setContext } from 'svelte';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { getMarkerId } from '@xyflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
import { BezierEdgeInternal } from '$lib/components/edges';
|
||||
import type { EdgeLayouted, Edge } from '$lib/types';
|
||||
import { useHandleEdgeSelect } from '$lib/hooks/useHandleEdgeSelect';
|
||||
|
||||
type $$Props = EdgeLayouted;
|
||||
import type { EdgeLayouted, Edge, EdgeEvents } from '$lib/types';
|
||||
|
||||
export let id: $$Props['id'];
|
||||
export let type: $$Props['type'] = 'default';
|
||||
export let source: $$Props['source'] = '';
|
||||
export let target: $$Props['target'] = '';
|
||||
export let data: $$Props['data'] = {};
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let zIndex: $$Props['zIndex'] = undefined;
|
||||
|
||||
export let animated: $$Props['animated'] = false;
|
||||
export let selected: $$Props['selected'] = false;
|
||||
export let selectable: $$Props['selectable'] = undefined;
|
||||
export let deletable: $$Props['deletable'] = undefined;
|
||||
export let hidden: $$Props['hidden'] = false;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
export let labelStyle: $$Props['labelStyle'] = undefined;
|
||||
export let markerStart: $$Props['markerStart'] = undefined;
|
||||
export let markerEnd: $$Props['markerEnd'] = undefined;
|
||||
export let sourceHandle: $$Props['sourceHandle'] = undefined;
|
||||
export let targetHandle: $$Props['targetHandle'] = undefined;
|
||||
export let sourceX: $$Props['sourceX'];
|
||||
export let sourceY: $$Props['sourceY'];
|
||||
export let targetX: $$Props['targetX'];
|
||||
export let targetY: $$Props['targetY'];
|
||||
export let sourcePosition: $$Props['sourcePosition'];
|
||||
export let targetPosition: $$Props['targetPosition'];
|
||||
export let ariaLabel: $$Props['ariaLabel'] = undefined;
|
||||
export let interactionWidth: $$Props['interactionWidth'] = undefined;
|
||||
|
||||
// TODO SVELTE5
|
||||
|
||||
// @ todo: support edge updates
|
||||
let className: string = '';
|
||||
export { className as class };
|
||||
const {
|
||||
id,
|
||||
type = 'default',
|
||||
source,
|
||||
target,
|
||||
data = {},
|
||||
style,
|
||||
zIndex,
|
||||
animated = false,
|
||||
selected = false,
|
||||
selectable,
|
||||
deletable,
|
||||
hidden,
|
||||
label,
|
||||
labelStyle,
|
||||
markerStart,
|
||||
markerEnd,
|
||||
sourceHandle,
|
||||
targetHandle,
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
ariaLabel,
|
||||
interactionWidth,
|
||||
class: className,
|
||||
onedgeclick,
|
||||
onedgecontextmenu,
|
||||
onedgemouseenter,
|
||||
onedgemouseleave
|
||||
}: EdgeLayouted & EdgeEvents = $props();
|
||||
|
||||
setContext('svelteflow__edge_id', id);
|
||||
|
||||
const { edgeLookup, edgeTypes, flowId, elementsSelectable } = useStore();
|
||||
const dispatch = createEventDispatcher<{
|
||||
edgeclick: { edge: Edge; event: MouseEvent | TouchEvent };
|
||||
edgecontextmenu: { edge: Edge; event: MouseEvent };
|
||||
edgemouseenter: { edge: Edge; event: MouseEvent };
|
||||
edgemouseleave: { edge: Edge; event: MouseEvent };
|
||||
}>();
|
||||
|
||||
$: edgeType = type || 'default';
|
||||
$: edgeComponent = $edgeTypes[edgeType] || BezierEdgeInternal;
|
||||
$: markerStartUrl = markerStart ? `url('#${getMarkerId(markerStart, $flowId)}')` : undefined;
|
||||
$: markerEndUrl = markerEnd ? `url('#${getMarkerId(markerEnd, $flowId)}')` : undefined;
|
||||
$: isSelectable = selectable ?? $elementsSelectable;
|
||||
let edgeType = $derived(type ?? 'default');
|
||||
let EdgeComponent = $derived($edgeTypes[edgeType] ?? BezierEdgeInternal);
|
||||
let markerStartUrl = $derived(
|
||||
markerStart ? `url('#${getMarkerId(markerStart, $flowId)}')` : undefined
|
||||
);
|
||||
let markerEndUrl = $derived(markerEnd ? `url('#${getMarkerId(markerEnd, $flowId)}')` : undefined);
|
||||
let isSelectable = $derived(selectable ?? $elementsSelectable);
|
||||
|
||||
const handleEdgeSelect = useHandleEdgeSelect();
|
||||
|
||||
function onClick(event: MouseEvent | TouchEvent) {
|
||||
function onclick(event: MouseEvent | TouchEvent) {
|
||||
const edge = $edgeLookup.get(id);
|
||||
|
||||
if (edge) {
|
||||
handleEdgeSelect(id);
|
||||
dispatch('edgeclick', { event, edge });
|
||||
onedgeclick?.({ event, edge });
|
||||
}
|
||||
}
|
||||
|
||||
type EdgeMouseEvent = 'edgecontextmenu' | 'edgemouseenter' | 'edgemouseleave';
|
||||
function onMouseEvent(event: MouseEvent, type: EdgeMouseEvent) {
|
||||
function onMouseEvent(
|
||||
event: MouseEvent,
|
||||
callback: ({ edge, event }: { edge: Edge; event: MouseEvent }) => void
|
||||
) {
|
||||
const edge = $edgeLookup.get(id);
|
||||
|
||||
if (edge) {
|
||||
dispatch(type, { event, edge });
|
||||
callback({ event, edge });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
||||
{#if !hidden}
|
||||
<svg style:z-index={zIndex}>
|
||||
<g
|
||||
@@ -93,16 +89,22 @@
|
||||
class:selected
|
||||
class:selectable={isSelectable}
|
||||
data-id={id}
|
||||
on:click={onClick}
|
||||
on:contextmenu={(e) => {
|
||||
onMouseEvent(e, 'edgecontextmenu');
|
||||
}}
|
||||
on:mouseenter={(e) => {
|
||||
onMouseEvent(e, 'edgemouseenter');
|
||||
}}
|
||||
on:mouseleave={(e) => {
|
||||
onMouseEvent(e, 'edgemouseleave');
|
||||
}}
|
||||
{onclick}
|
||||
oncontextmenu={onedgecontextmenu
|
||||
? (e) => {
|
||||
onMouseEvent(e, onedgecontextmenu);
|
||||
}
|
||||
: undefined}
|
||||
onmouseenter={onedgemouseenter
|
||||
? (e) => {
|
||||
onMouseEvent(e, onedgemouseenter);
|
||||
}
|
||||
: undefined}
|
||||
onmouseleave={onedgemouseleave
|
||||
? (e) => {
|
||||
onMouseEvent(e, onedgemouseleave);
|
||||
}
|
||||
: undefined}
|
||||
aria-label={ariaLabel === null
|
||||
? undefined
|
||||
: ariaLabel
|
||||
@@ -110,8 +112,7 @@
|
||||
: `Edge from ${source} to ${target}`}
|
||||
role="img"
|
||||
>
|
||||
<svelte:component
|
||||
this={edgeComponent}
|
||||
<EdgeComponent
|
||||
{id}
|
||||
{source}
|
||||
{target}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
//TODO SVELTE5
|
||||
import { getContext } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import cc from 'classcat';
|
||||
import {
|
||||
Position,
|
||||
@@ -15,34 +13,32 @@
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
import type { HandleProps } from '$lib/types';
|
||||
import type { ConnectableContext } from '../NodeWrapper/types';
|
||||
|
||||
type $$Props = HandleProps;
|
||||
|
||||
export let id: $$Props['id'] = undefined;
|
||||
export let type: $$Props['type'] = 'source';
|
||||
export let position: $$Props['position'] = Position.Top;
|
||||
export let style: $$Props['style'] = undefined;
|
||||
export let isValidConnection: $$Props['isValidConnection'] = undefined;
|
||||
export let onconnect: $$Props['onconnect'] = undefined;
|
||||
export let ondisconnect: $$Props['ondisconnect'] = undefined;
|
||||
let {
|
||||
id: handleId = null,
|
||||
type = 'source',
|
||||
position = Position.Top,
|
||||
style,
|
||||
class: className,
|
||||
isConnectable: isConnectableProp,
|
||||
isValidConnection: isValidConnectionProp,
|
||||
onconnect,
|
||||
ondisconnect,
|
||||
children
|
||||
}: HandleProps = $props();
|
||||
// @todo implement connectablestart, connectableend
|
||||
// export let isConnectableStart: $$Props['isConnectableStart'] = undefined;
|
||||
// export let isConnectableEnd: $$Props['isConnectableEnd'] = undefined;
|
||||
|
||||
let isConnectableProp: $$Props['isConnectable'] = undefined;
|
||||
export { isConnectableProp as isConnectable };
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
|
||||
$: isTarget = type === 'target';
|
||||
const nodeId = getContext<string>('svelteflow__node_id');
|
||||
const connectable = getContext<Writable<boolean>>('svelteflow__node_connectable');
|
||||
$: isConnectable = isConnectableProp !== undefined ? isConnectableProp : $connectable;
|
||||
const isConnectableContext = getContext<ConnectableContext>('svelteflow__node_connectable');
|
||||
|
||||
$: handleId = id || null;
|
||||
let isTarget = $derived(type === 'target');
|
||||
let isConnectable = $derived(
|
||||
isConnectableProp !== undefined ? isConnectableProp : isConnectableContext.value
|
||||
);
|
||||
|
||||
const store = useStore();
|
||||
const {
|
||||
connectionMode,
|
||||
domNode,
|
||||
@@ -64,7 +60,7 @@
|
||||
onconnectend: onConnectEndAction,
|
||||
flowId,
|
||||
connection
|
||||
} = store;
|
||||
} = useStore();
|
||||
|
||||
function onPointerDown(event: MouseEvent | TouchEvent) {
|
||||
const isMouseTriggered = isMouseEvent(event);
|
||||
@@ -81,7 +77,7 @@
|
||||
lib: $lib,
|
||||
autoPanOnConnect: $autoPanOnConnect,
|
||||
flowId: $flowId,
|
||||
isValidConnection: isValidConnection ?? $isValidConnectionStore,
|
||||
isValidConnection: isValidConnectionProp ?? $isValidConnectionStore,
|
||||
updateConnection,
|
||||
cancelConnection,
|
||||
panBy,
|
||||
@@ -112,39 +108,45 @@
|
||||
}
|
||||
|
||||
let prevConnections: Map<string, HandleConnection> | null = null;
|
||||
let connections: Map<string, HandleConnection> | undefined;
|
||||
|
||||
$: if (onconnect || ondisconnect) {
|
||||
$effect.pre(() => {
|
||||
// connectionLookup is not reactive, so we use edges to get notified about updates
|
||||
$edges;
|
||||
connections = $connectionLookup.get(`${nodeId}-${type}-${id || null}`);
|
||||
}
|
||||
if (onconnect || ondisconnect) {
|
||||
let connections = $connectionLookup.get(`${nodeId}-${type}-${handleId}`);
|
||||
|
||||
$: {
|
||||
if (prevConnections && !areConnectionMapsEqual(connections, prevConnections)) {
|
||||
const _connections = connections ?? new Map();
|
||||
if (prevConnections && !areConnectionMapsEqual(connections, prevConnections)) {
|
||||
const _connections = connections ?? new Map();
|
||||
|
||||
handleConnectionChange(prevConnections, _connections, ondisconnect);
|
||||
handleConnectionChange(_connections, prevConnections, onconnect);
|
||||
handleConnectionChange(prevConnections, _connections, ondisconnect);
|
||||
handleConnectionChange(_connections, prevConnections, onconnect);
|
||||
}
|
||||
|
||||
prevConnections = new Map(connections);
|
||||
}
|
||||
});
|
||||
|
||||
prevConnections = connections ?? new Map();
|
||||
}
|
||||
let [connectionInProcess, connectingFrom, connectingTo, isPossibleEndHandle, valid] = $derived.by(
|
||||
() => {
|
||||
const { fromHandle, toHandle, isValid } = $connection;
|
||||
|
||||
$: connectionInProcess = !!$connection.fromHandle;
|
||||
$: connectingFrom =
|
||||
$connection.fromHandle?.nodeId === nodeId &&
|
||||
$connection.fromHandle?.type === type &&
|
||||
$connection.fromHandle?.id === handleId;
|
||||
$: connectingTo =
|
||||
$connection.toHandle?.nodeId === nodeId &&
|
||||
$connection.toHandle?.type === type &&
|
||||
$connection.toHandle?.id === handleId;
|
||||
$: isPossibleEndHandle =
|
||||
$connectionMode === ConnectionMode.Strict
|
||||
? $connection.fromHandle?.type !== type
|
||||
: nodeId !== $connection.fromHandle?.nodeId || handleId !== $connection.fromHandle?.id;
|
||||
$: valid = connectingTo && $connection.isValid;
|
||||
const connectionInProcess = !!fromHandle;
|
||||
|
||||
const connectingFrom =
|
||||
fromHandle?.nodeId === nodeId && fromHandle?.type === type && fromHandle?.id === handleId;
|
||||
|
||||
const connectingTo =
|
||||
toHandle?.nodeId === nodeId && toHandle?.type === type && toHandle?.id === handleId;
|
||||
|
||||
const isPossibleEndHandle =
|
||||
$connectionMode === ConnectionMode.Strict
|
||||
? fromHandle?.type !== type
|
||||
: nodeId !== fromHandle?.nodeId || handleId !== fromHandle?.id;
|
||||
|
||||
const valid = connectingTo && isValid;
|
||||
|
||||
return [connectionInProcess, connectingFrom, connectingTo, isPossibleEndHandle, valid];
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<!--
|
||||
@@ -155,7 +157,7 @@ The Handle component is the part of a node that can be used to connect nodes.
|
||||
data-handleid={handleId}
|
||||
data-nodeid={nodeId}
|
||||
data-handlepos={position}
|
||||
data-id="{$flowId}-{nodeId}-{id || null}-{type}"
|
||||
data-id="{$flowId}-{nodeId}-{handleId}-{type}"
|
||||
class={cc([
|
||||
'svelte-flow__handle',
|
||||
`svelte-flow__handle-${position}`,
|
||||
@@ -173,11 +175,11 @@ The Handle component is the part of a node that can be used to connect nodes.
|
||||
class:connectableend={isConnectable}
|
||||
class:connectable={isConnectable}
|
||||
class:connectionindicator={isConnectable && (!connectionInProcess || isPossibleEndHandle)}
|
||||
on:mousedown={onPointerDown}
|
||||
on:touchstart={onPointerDown}
|
||||
onmousedown={onPointerDown}
|
||||
ontouchstart={onPointerDown}
|
||||
{style}
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import drag from '$lib/actions/drag';
|
||||
import { useStore } from '$lib/store';
|
||||
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
||||
import type { NodeWrapperProps } from './types';
|
||||
import type { ConnectableContext, NodeWrapperProps } from './types';
|
||||
import { getNodeInlineStyleDimensions } from './utils';
|
||||
import type { NodeEvents } from '$lib/types';
|
||||
|
||||
@@ -64,14 +64,20 @@
|
||||
let nodeRef: HTMLDivElement | null = $state(null);
|
||||
let prevNodeRef: HTMLDivElement | null = null;
|
||||
|
||||
const connectableStore = writable(connectable);
|
||||
|
||||
let prevType: string | undefined;
|
||||
let prevSourcePosition: Position | undefined;
|
||||
let prevTargetPosition: Position | undefined;
|
||||
|
||||
let NodeComponent = $derived($nodeTypes[type] ?? DefaultNode);
|
||||
|
||||
let connectableContext: ConnectableContext = {
|
||||
get value() {
|
||||
return connectable;
|
||||
}
|
||||
};
|
||||
setContext('svelteflow__node_connectable', connectableContext);
|
||||
setContext('svelteflow__node_id', id);
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
$effect(() => {
|
||||
const valid = !!$nodeTypes[type];
|
||||
@@ -92,10 +98,6 @@
|
||||
})
|
||||
);
|
||||
|
||||
$effect(() => {
|
||||
connectableStore.set(!!connectable);
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
// if type, sourcePosition or targetPosition changes,
|
||||
// we need to re-calculate the handle positions
|
||||
@@ -128,9 +130,6 @@
|
||||
prevTargetPosition = targetPosition;
|
||||
});
|
||||
|
||||
setContext('svelteflow__node_id', id);
|
||||
setContext('svelteflow__node_connectable', connectableStore);
|
||||
|
||||
// TODO: extract this part!
|
||||
$effect(() => {
|
||||
// TODO: HOLY MOLY! changing the order of the initialized breaks effect subscriptions
|
||||
@@ -215,7 +214,7 @@
|
||||
{dragHandle}
|
||||
{parentId}
|
||||
{type}
|
||||
isConnectable={$connectableStore}
|
||||
isConnectable={connectable}
|
||||
positionAbsoluteX={positionX}
|
||||
positionAbsoluteY={positionY}
|
||||
{width}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { InternalNode, Node } from '$lib/types';
|
||||
|
||||
export type ConnectableContext = {
|
||||
value: boolean;
|
||||
};
|
||||
|
||||
export type NodeWrapperProps = Pick<
|
||||
Node,
|
||||
| 'id'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { ShortcutModifierDefinition } from '@svelte-put/shortcut';
|
||||
import type {
|
||||
FitViewOptionsBase,
|
||||
@@ -27,6 +28,7 @@ export type HandleProps = HandlePropsSystem & {
|
||||
style?: string;
|
||||
onconnect?: (connections: Connection[]) => void;
|
||||
ondisconnect?: (connections: Connection[]) => void;
|
||||
children?: Snippet;
|
||||
};
|
||||
|
||||
export type FitViewOptions<NodeType extends Node = Node> = FitViewOptionsBase<NodeType>;
|
||||
|
||||
@@ -36,5 +36,5 @@ export type HandleProps = {
|
||||
/** Id of the handle
|
||||
* @remarks optional if there is only one handle of this type
|
||||
*/
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user