change all store states to state.raw to prevent proxying all objects

This commit is contained in:
peterkogo
2025-06-12 13:37:10 +02:00
parent 74599bb8dc
commit 28890a09d6
3 changed files with 125 additions and 16 deletions
@@ -24,6 +24,7 @@
import '@xyflow/svelte/dist/style.css';
import InitTracker from './InitTracker.svelte';
import TriggerConnection from './TriggerConnection.svelte';
const nodeTypes: NodeTypes = {
custom: CustomNode,
@@ -243,6 +244,7 @@
}));
}}>left-right</button
>
<TriggerConnection />
</Panel>
<InitTracker />
</SvelteFlow>
@@ -0,0 +1,107 @@
<script lang="ts">
import { useConnection, useViewport } from '@xyflow/svelte';
const connection = useConnection();
const viewport = useViewport();
let blockMouseUp = true;
// Replace this with the flowId of your flow (default is 1)
const flowId = 1;
/** Replace this selector with the handle of your choice.
* Take a look at the DOM structure to see what how the data-*
* attributes are structured for handles
*/
const fromHandleSelector = '[data-nodeid="1"].svelte-flow__handle';
function createPointerEvent(type: string, element: HTMLElement) {
const { left, top } = element.getBoundingClientRect();
return new PointerEvent(type, {
bubbles: true,
cancelable: true,
pointerId: 1,
pointerType: 'mouse',
clientX: left + element.offsetWidth / 2,
clientY: top + element.offsetHeight / 2,
button: 0,
buttons: 1
});
}
function mouseDownBlocker(event: MouseEvent) {
event.preventDefault();
event.stopPropagation();
}
function mouseUpBlocker(event: MouseEvent) {
// we only want to block the original mouseUpEvent
if (!blockMouseUp) {
return;
}
event.preventDefault();
event.stopPropagation();
connectionFinish();
}
function addEventListeners() {
document.addEventListener('mousedown', mouseDownBlocker, true);
document.addEventListener('mouseup', mouseUpBlocker, true);
}
function removeEventListeners() {
document.removeEventListener('mousedown', mouseDownBlocker, true);
document.removeEventListener('mouseup', mouseUpBlocker, true);
}
function connectionFinish() {
blockMouseUp = false;
const { inProgress, isValid, toHandle } = connection.current;
if (inProgress && isValid && toHandle) {
const toHandleElement = document.querySelector(
`[data-id="${flowId}-${toHandle.nodeId}-${toHandle.id ?? null}-${toHandle.type}"].svelte-flow__handle`
) as HTMLElement | null;
if (toHandleElement) {
const mouseupEvent = createPointerEvent('mouseup', toHandleElement);
const success = toHandleElement.dispatchEvent(mouseupEvent);
if (success) {
removeEventListeners();
return;
}
}
}
connectionCancel();
}
function connectionStart() {
const fromHandle = document.querySelector(fromHandleSelector) as HTMLElement | null;
if (fromHandle) {
const mousedownEvent = createPointerEvent('mousedown', fromHandle);
const success = fromHandle.dispatchEvent(mousedownEvent);
if (success) {
blockMouseUp = true;
addEventListeners();
}
}
}
function connectionCancel() {
const fromHandle = document.querySelector(fromHandleSelector) as HTMLElement | null;
if (fromHandle) {
const mouseUpEvent = createPointerEvent('mouseup', fromHandle);
fromHandle.dispatchEvent(mouseUpEvent);
}
removeEventListeners();
}
</script>
<!-- onclick works as well -->
<button onpointerdown={connectionStart}>trigger-connection</button>
@@ -110,10 +110,10 @@ export function getInitialStore<NodeType extends Node = Node, EdgeType extends E
// Inline classes have some performance implications but we just call it once (max twice).
class SvelteFlowStore {
flowId: string = $derived(signals.props.id ?? '1');
domNode = $state<HTMLDivElement | null>(null);
panZoom: PanZoomInstance | null = $state(null);
width = $state<number>(signals.width ?? 0);
height = $state<number>(signals.height ?? 0);
domNode = $state.raw<HTMLDivElement | null>(null);
panZoom: PanZoomInstance | null = $state.raw(null);
width = $state.raw<number>(signals.width ?? 0);
height = $state.raw<number>(signals.height ?? 0);
nodesInitialized: boolean = $derived.by(() => {
const nodesInitialized = adoptUserNodes(signals.nodes, this.nodeLookup, this.parentLookup, {
@@ -293,16 +293,16 @@ export function getInitialStore<NodeType extends Node = Node, EdgeType extends E
snapGrid: SnapGrid | null = $derived(signals.props.snapGrid ?? null);
dragging: boolean = $state(false);
selectionRect: SelectionRect | null = $state(null);
dragging: boolean = $state.raw(false);
selectionRect: SelectionRect | null = $state.raw(null);
selectionKeyPressed: boolean = $state(false);
multiselectionKeyPressed: boolean = $state(false);
deleteKeyPressed: boolean = $state(false);
panActivationKeyPressed: boolean = $state(false);
zoomActivationKeyPressed: boolean = $state(false);
selectionRectMode: string | null = $state(null);
ariaLiveMessage = $state<string>('');
selectionKeyPressed: boolean = $state.raw(false);
multiselectionKeyPressed: boolean = $state.raw(false);
deleteKeyPressed: boolean = $state.raw(false);
panActivationKeyPressed: boolean = $state.raw(false);
zoomActivationKeyPressed: boolean = $state.raw(false);
selectionRectMode: string | null = $state.raw(null);
ariaLiveMessage = $state.raw<string>('');
selectionMode: SelectionMode = $derived(signals.props.selectionMode ?? SelectionMode.Partial);
nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes });
@@ -317,7 +317,7 @@ export function getInitialStore<NodeType extends Node = Node, EdgeType extends E
// _viewport is the internal viewport.
// when binding to viewport, we operate on signals.viewport instead
_viewport: Viewport = $state(
_viewport: Viewport = $state.raw(
getInitialViewport(
this.nodesInitialized,
signals.props.fitView,
@@ -338,7 +338,7 @@ export function getInitialStore<NodeType extends Node = Node, EdgeType extends E
}
// _connection is viewport independent and originating from XYHandle
_connection: ConnectionState = $state(initialConnection);
_connection: ConnectionState = $state.raw(initialConnection);
// We derive a viewport dependent connection here
connection: ConnectionState = $derived.by(() => {
if (this._connection.inProgress) {
@@ -392,7 +392,7 @@ export function getInitialStore<NodeType extends Node = Node, EdgeType extends E
clickConnect?: boolean = $derived(signals.props.clickConnect ?? true);
onclickconnectstart?: OnConnectStart = $derived(signals.props.onclickconnectstart);
onclickconnectend?: OnConnectEnd = $derived(signals.props.onclickconnectend);
clickConnectStartHandle: Pick<Handle, 'id' | 'nodeId' | 'type'> | null = $state(null);
clickConnectStartHandle: Pick<Handle, 'id' | 'nodeId' | 'type'> | null = $state.raw(null);
onselectiondrag?: OnSelectionDrag<NodeType> = $derived(signals.props.onselectiondrag);
onselectiondragstart?: OnSelectionDrag<NodeType> = $derived(signals.props.onselectiondragstart);