Merge pull request #3811 from xyflow/svelte-on-init

Added onInit and useInitialized hooks
This commit is contained in:
Moritz Klack
2024-01-23 15:53:06 +01:00
committed by GitHub
14 changed files with 137 additions and 6 deletions
@@ -22,6 +22,7 @@
import CustomEdge from './CustomEdge.svelte'; import CustomEdge from './CustomEdge.svelte';
import '@xyflow/svelte/dist/style.css'; import '@xyflow/svelte/dist/style.css';
import InitTracker from './InitTracker.svelte';
const nodeTypes: NodeTypes = { const nodeTypes: NodeTypes = {
custom: CustomNode, custom: CustomNode,
@@ -148,6 +149,7 @@
selectionMode={SelectionMode.Full} selectionMode={SelectionMode.Full}
initialViewport={{ x: 100, y: 100, zoom: 2 }} initialViewport={{ x: 100, y: 100, zoom: 2 }}
snapGrid={[25, 25]} snapGrid={[25, 25]}
oninit={() => console.log('on init')}
on:nodeclick={(event) => console.log('on node click', event)} on:nodeclick={(event) => console.log('on node click', event)}
on:nodemouseenter={(event) => console.log('on node enter', event)} on:nodemouseenter={(event) => console.log('on node enter', event)}
on:nodemouseleave={(event) => console.log('on node leave', event)} on:nodemouseleave={(event) => console.log('on node leave', event)}
@@ -207,6 +209,8 @@
}}>hide/unhide</button }}>hide/unhide</button
> >
</Panel> </Panel>
<InitTracker />
</SvelteFlow> </SvelteFlow>
<style> <style>
@@ -0,0 +1,18 @@
<script lang="ts">
import { useNodesInitialized, useInitialized } from '@xyflow/svelte';
const nodesInitialized = useNodesInitialized();
const initialized = useInitialized();
$: {
if (nodesInitialized) {
console.log('nodes initialized');
}
}
$: {
if (initialized) {
console.log('initialized');
}
}
</script>
@@ -12,9 +12,15 @@ const selector = (options: UseNodesInitializedOptions) => (s: ReactFlowState) =>
return false; return false;
} }
return s.nodes for (const node of s.nodes) {
.filter((n) => (options.includeHiddenNodes ? true : !n.hidden)) if (options.includeHiddenNodes || !node.hidden) {
.every((n) => n[internalsSymbol]?.handleBounds !== undefined); if (node[internalsSymbol]?.handleBounds === undefined) {
return false;
}
}
}
return true;
}; };
const defaultOptions = { const defaultOptions = {
+1
View File
@@ -5,6 +5,7 @@
## Minor changes ## Minor changes
- add `getNode`, `getNodes`, `getEdge` and `getEdges` to `useSvelteFlow` - add `getNode`, `getNodes`, `getEdge` and `getEdges` to `useSvelteFlow`
- add `useInitialized` / `useNNodesInitialized` hooks and `oninit` handler
## Patch changes ## Patch changes
@@ -0,0 +1,14 @@
<script lang="ts">
import { onMount } from 'svelte';
let _onMount: (() => void) | undefined = undefined;
export { _onMount as onMount };
let _onDestroy: (() => void) | undefined = undefined;
export { _onDestroy as onDestroy };
onMount(() => {
_onMount?.();
return _onDestroy;
});
</script>
@@ -0,0 +1 @@
export { default as CallOnMount } from './CallOnMount.svelte';
@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { EdgeWrapper } from '$lib/components/EdgeWrapper'; import { EdgeWrapper } from '$lib/components/EdgeWrapper';
import { CallOnMount } from '$lib/components/CallOnMount';
import { MarkerDefinition } from '$lib/container/EdgeRenderer/MarkerDefinition'; import { MarkerDefinition } from '$lib/container/EdgeRenderer/MarkerDefinition';
import { useStore } from '$lib/store'; import { useStore } from '$lib/store';
import type { DefaultEdgeOptions } from '$lib/types'; import type { DefaultEdgeOptions } from '$lib/types';
@@ -8,8 +9,8 @@
export let defaultEdgeOptions: DefaultEdgeOptions | undefined; export let defaultEdgeOptions: DefaultEdgeOptions | undefined;
const { const {
elementsSelectable,
visibleEdges, visibleEdges,
edgesInitialized,
edges: { setDefaultOptions } edges: { setDefaultOptions }
} = useStore(); } = useStore();
@@ -54,4 +55,15 @@
on:edgecontextmenu on:edgecontextmenu
/> />
{/each} {/each}
{#if $visibleEdges.length > 0}
<CallOnMount
onMount={() => {
$edgesInitialized = true;
}}
onDestroy={() => {
$edgesInitialized = false;
}}
/>
{/if}
</div> </div>
@@ -77,6 +77,7 @@
export let onconnectstart: $$Props['onconnectstart'] = undefined; export let onconnectstart: $$Props['onconnectstart'] = undefined;
export let onconnectend: $$Props['onconnectend'] = undefined; export let onconnectend: $$Props['onconnectend'] = undefined;
export let onbeforedelete: $$Props['onbeforedelete'] = undefined; export let onbeforedelete: $$Props['onbeforedelete'] = undefined;
export let oninit: $$Props['oninit'] = undefined;
export let defaultMarkerColor = '#b1b1b7'; export let defaultMarkerColor = '#b1b1b7';
@@ -130,6 +131,16 @@
} }
} }
// Call oninit once when flow is intialized
const { initialized } = store;
let onInitCalled = false;
$: {
if (!onInitCalled && $initialized) {
oninit?.();
onInitCalled = true;
}
}
// this updates the store for simple changes // this updates the store for simple changes
// where the prop names equals the store name // where the prop names equals the store name
$: { $: {
@@ -333,4 +333,6 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
onconnectstart?: OnConnectStart; onconnectstart?: OnConnectStart;
/** When a user stops dragging a connection line, this event gets fired. */ /** When a user stops dragging a connection line, this event gets fired. */
onconnectend?: OnConnectEnd; onconnectend?: OnConnectEnd;
/** This handler gets called when the flow is finished initializing */
oninit?: () => void;
}; };
@@ -4,6 +4,7 @@
import { useStore } from '$lib/store'; import { useStore } from '$lib/store';
import zoom from '$lib/actions/zoom'; import zoom from '$lib/actions/zoom';
import type { ZoomProps } from './types'; import type { ZoomProps } from './types';
import { onMount } from 'svelte';
type $$Props = ZoomProps; type $$Props = ZoomProps;
@@ -29,12 +30,17 @@
translateExtent, translateExtent,
lib, lib,
panActivationKeyPressed, panActivationKeyPressed,
zoomActivationKeyPressed zoomActivationKeyPressed,
viewportInitialized
} = useStore(); } = useStore();
$: viewPort = initialViewport || { x: 0, y: 0, zoom: 1 }; $: viewPort = initialViewport || { x: 0, y: 0, zoom: 1 };
$: _panOnDrag = $panActivationKeyPressed || panOnDrag; $: _panOnDrag = $panActivationKeyPressed || panOnDrag;
$: _panOnScroll = $panActivationKeyPressed || panOnScroll; $: _panOnScroll = $panActivationKeyPressed || panOnScroll;
onMount(() => {
$viewportInitialized = true;
});
</script> </script>
<div <div
@@ -0,0 +1,24 @@
import { useStore } from '$lib/store';
import type { Readable } from 'svelte/store';
/**
* Hook for seeing if nodes are initialized
* @returns - nodesInitialized Writable
*/
export function useNodesInitialized() {
const { nodesInitialized } = useStore();
return {
subscribe: nodesInitialized.subscribe
} as Readable<boolean>;
}
/**
* Hook for seeing if the flow is initialized
* @returns - initialized Writable
*/
export function useInitialized() {
const { initialized } = useStore();
return {
subscribe: initialized.subscribe
} as Readable<boolean>;
}
+1
View File
@@ -31,6 +31,7 @@ export * from '$lib/hooks/useConnection';
export * from '$lib/hooks/useNodesEdges'; export * from '$lib/hooks/useNodesEdges';
export * from '$lib/hooks/useHandleConnections'; export * from '$lib/hooks/useHandleConnections';
export * from '$lib/hooks/useNodesData'; export * from '$lib/hooks/useNodesData';
export { useInitialized, useNodesInitialized } from '$lib/hooks/useInitialized';
// types // types
export type { export type {
+27
View File
@@ -111,6 +111,10 @@ export function createStore({
} }
store.nodes.set(nextNodes); store.nodes.set(nextNodes);
if (!get(store.nodesInitialized)) {
store.nodesInitialized.set(true);
}
} }
function fitView(nodes: Node[], options?: FitViewOptions) { function fitView(nodes: Node[], options?: FitViewOptions) {
@@ -353,6 +357,29 @@ export function createStore({
[store.edges, store.defaultMarkerColor, store.flowId], [store.edges, store.defaultMarkerColor, store.flowId],
([edges, defaultColor, id]) => createMarkerIds(edges, { defaultColor, id }) ([edges, defaultColor, id]) => createMarkerIds(edges, { defaultColor, id })
), ),
initialized: (() => {
let initialized = false;
const initialNodesLength = get(store.nodes).length;
const initialEdgesLength = get(store.edges).length;
return derived(
[store.nodesInitialized, store.edgesInitialized, store.viewportInitialized],
([nodesInitialized, edgesInitialized, viewportInitialized]) => {
// If it was already initialized, return true from then on
if (initialized) return initialized;
// if it hasn't been initialised check if it's now
if (initialNodesLength === 0) {
initialized = viewportInitialized;
} else if (initialEdgesLength === 0) {
initialized = viewportInitialized && nodesInitialized;
} else {
initialized = viewportInitialized && nodesInitialized && edgesInitialized;
}
return initialized;
}
);
})(),
// actions // actions
syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes), syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes),
@@ -152,6 +152,10 @@ export const getInitialStore = ({
onconnect: writable<OnConnect>(undefined), onconnect: writable<OnConnect>(undefined),
onconnectstart: writable<OnConnectStart>(undefined), onconnectstart: writable<OnConnectStart>(undefined),
onconnectend: writable<OnConnectEnd>(undefined), onconnectend: writable<OnConnectEnd>(undefined),
onbeforedelete: writable<OnBeforeDelete>(undefined) onbeforedelete: writable<OnBeforeDelete>(undefined),
nodesInitialized: writable<boolean>(false),
edgesInitialized: writable<boolean>(false),
viewportInitialized: writable<boolean>(false),
initialized: readable<boolean>(false)
}; };
}; };