migrate nodetoolbar
This commit is contained in:
@@ -38,5 +38,5 @@ export type NodeWrapperProps = Pick<
|
|||||||
zIndex: number;
|
zIndex: number;
|
||||||
node: InternalNode;
|
node: InternalNode;
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
nodeClickDistance: number;
|
nodeClickDistance?: number;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,13 +2,21 @@
|
|||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import { nodeHasDimensions } from '@xyflow/system';
|
import { nodeHasDimensions } from '@xyflow/system';
|
||||||
|
|
||||||
|
import type { NodeEvents } from '$lib/types/events';
|
||||||
import { NodeWrapper } from '$lib/components/NodeWrapper';
|
import { NodeWrapper } from '$lib/components/NodeWrapper';
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import type { NodeRendererProps } from './types';
|
|
||||||
|
|
||||||
type $$Props = NodeRendererProps;
|
let {
|
||||||
|
nodeClickDistance,
|
||||||
export let nodeClickDistance: $$Props['nodeClickDistance'] = 0;
|
onnodeclick,
|
||||||
|
onnodecontextmenu,
|
||||||
|
onnodemouseenter,
|
||||||
|
onnodemousemove,
|
||||||
|
onnodemouseleave,
|
||||||
|
onnodedrag,
|
||||||
|
onnodedragstart,
|
||||||
|
onnodedragstop
|
||||||
|
}: { nodeClickDistance?: number } & NodeEvents = $props();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
visibleNodes,
|
visibleNodes,
|
||||||
@@ -82,14 +90,14 @@
|
|||||||
parentId={node.parentId}
|
parentId={node.parentId}
|
||||||
{resizeObserver}
|
{resizeObserver}
|
||||||
{nodeClickDistance}
|
{nodeClickDistance}
|
||||||
on:nodeclick
|
{onnodeclick}
|
||||||
on:nodemouseenter
|
{onnodemouseenter}
|
||||||
on:nodemousemove
|
{onnodemousemove}
|
||||||
on:nodemouseleave
|
{onnodemouseleave}
|
||||||
on:nodedrag
|
{onnodedrag}
|
||||||
on:nodedragstart
|
{onnodedragstart}
|
||||||
on:nodedragstop
|
{onnodedragstop}
|
||||||
on:nodecontextmenu
|
{onnodecontextmenu}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export type NodeRendererProps = {
|
|
||||||
nodeClickDistance?: number;
|
|
||||||
};
|
|
||||||
@@ -1,38 +1,32 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
import { Position, getNodeToolbarTransform } from '@xyflow/system';
|
import { Position, getNodeToolbarTransform } from '@xyflow/system';
|
||||||
import portal from '$lib/actions/portal';
|
|
||||||
import type { InternalNode } from '$lib/types';
|
|
||||||
import { useStore } from '$lib/store';
|
|
||||||
|
|
||||||
import type { NodeToolbarProps } from './types';
|
import portal from '$lib/actions/portal';
|
||||||
|
import { useStore } from '$lib/store';
|
||||||
import { useSvelteFlow } from '$lib/hooks/useSvelteFlow';
|
import { useSvelteFlow } from '$lib/hooks/useSvelteFlow';
|
||||||
|
|
||||||
type $$Props = NodeToolbarProps;
|
import type { InternalNode } from '$lib/types';
|
||||||
|
import type { NodeToolbarProps } from './types';
|
||||||
|
|
||||||
export let nodeId: $$Props['nodeId'] = undefined;
|
let {
|
||||||
export let position: $$Props['position'] = undefined;
|
nodeId,
|
||||||
export let align: $$Props['align'] = undefined;
|
position = Position.Top,
|
||||||
export let offset: $$Props['offset'] = undefined;
|
align = 'center',
|
||||||
export let isVisible: $$Props['isVisible'] = undefined;
|
offset = 10,
|
||||||
|
isVisible,
|
||||||
|
children
|
||||||
|
}: NodeToolbarProps = $props();
|
||||||
|
|
||||||
const { domNode, viewport, nodeLookup, nodes } = useStore();
|
const { domNode, viewport, nodeLookup, nodes } = useStore();
|
||||||
const { getNodesBounds } = useSvelteFlow();
|
const { getNodesBounds } = useSvelteFlow();
|
||||||
const contextNodeId = getContext<string>('svelteflow__node_id');
|
const contextNodeId = getContext<string>('svelteflow__node_id');
|
||||||
|
|
||||||
let transform: string;
|
let toolbarNodes: InternalNode[] = $derived.by(() => {
|
||||||
let toolbarNodes: InternalNode[] = [];
|
|
||||||
let _offset = offset !== undefined ? offset : 10;
|
|
||||||
let _position = position !== undefined ? position : Position.Top;
|
|
||||||
let _align = align !== undefined ? align : 'center';
|
|
||||||
|
|
||||||
$: {
|
|
||||||
// nly needed to trigger updates, $nodeLookup is just a helper that does not trigger any updates
|
|
||||||
$nodes;
|
$nodes;
|
||||||
|
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId ?? contextNodeId];
|
||||||
|
|
||||||
const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId];
|
return nodeIds.reduce<InternalNode[]>((res, nodeId) => {
|
||||||
|
|
||||||
toolbarNodes = nodeIds.reduce<InternalNode[]>((res, nodeId) => {
|
|
||||||
const node = $nodeLookup.get(nodeId);
|
const node = $nodeLookup.get(nodeId);
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
@@ -41,29 +35,31 @@
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}, []);
|
}, []);
|
||||||
}
|
});
|
||||||
|
|
||||||
$: {
|
let transform: string = $derived.by(() => {
|
||||||
const nodeRect = getNodesBounds(toolbarNodes);
|
const nodeRect = getNodesBounds(toolbarNodes);
|
||||||
|
|
||||||
if (nodeRect) {
|
if (nodeRect) {
|
||||||
transform = getNodeToolbarTransform(nodeRect, $viewport, _position, _offset, _align);
|
return getNodeToolbarTransform(nodeRect, $viewport, position, offset, align);
|
||||||
}
|
}
|
||||||
}
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
$: zIndex =
|
let zIndex = $derived(
|
||||||
toolbarNodes.length === 0
|
toolbarNodes.length === 0
|
||||||
? 1
|
? 1
|
||||||
: Math.max(...toolbarNodes.map((node) => (node.internals.z || 5) + 1));
|
: Math.max(...toolbarNodes.map((node) => (node.internals.z || 5) + 1))
|
||||||
|
);
|
||||||
|
|
||||||
//FIXME: Possible performance bottleneck
|
//FIXME: Possible performance bottleneck
|
||||||
$: selectedNodesCount = $nodes.filter((node) => node.selected).length;
|
let selectedNodesCount = $derived($nodes.filter((node) => node.selected).length);
|
||||||
|
|
||||||
// if isVisible is not set, we show the toolbar only if its node is selected and no other node is selected
|
// if isVisible is not set, we show the toolbar only if its node is selected and no other node is selected
|
||||||
$: isActive =
|
let isActive = $derived(
|
||||||
typeof isVisible === 'boolean'
|
typeof isVisible === 'boolean'
|
||||||
? isVisible
|
? isVisible
|
||||||
: toolbarNodes.length === 1 && toolbarNodes[0].selected && selectedNodesCount === 1;
|
: toolbarNodes.length === 1 && toolbarNodes[0].selected && selectedNodesCount === 1
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $domNode && isActive && toolbarNodes}
|
{#if $domNode && isActive && toolbarNodes}
|
||||||
@@ -75,6 +71,6 @@
|
|||||||
style:transform
|
style:transform
|
||||||
style:z-index={zIndex}
|
style:z-index={zIndex}
|
||||||
>
|
>
|
||||||
<slot />
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Position, Align } from '@xyflow/system';
|
import type { Position, Align } from '@xyflow/system';
|
||||||
|
import type { Snippet } from 'svelte';
|
||||||
|
|
||||||
export type NodeToolbarProps = {
|
export type NodeToolbarProps = {
|
||||||
/** The id of the node, or array of ids the toolbar should be displayed at */
|
/** The id of the node, or array of ids the toolbar should be displayed at */
|
||||||
@@ -16,4 +17,5 @@ export type NodeToolbarProps = {
|
|||||||
offset?: number;
|
offset?: number;
|
||||||
/** If true, node toolbar is visible even if node is not selected */
|
/** If true, node toolbar is visible even if node is not selected */
|
||||||
isVisible?: boolean;
|
isVisible?: boolean;
|
||||||
|
children?: Snippet;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user