diff --git a/packages/node-toolbar/src/NodeToolbar.vue b/packages/node-toolbar/src/NodeToolbar.vue index 89dee7b2..d8c2413e 100644 --- a/packages/node-toolbar/src/NodeToolbar.vue +++ b/packages/node-toolbar/src/NodeToolbar.vue @@ -4,43 +4,58 @@ import type { GraphNode, Rect, ViewportTransform } from '@vue-flow/core' import { NodeIdInjection, Position, getRectOfNodes, useVueFlow } from '@vue-flow/core' import type { CSSProperties } from 'vue' -import type { NodeToolbarProps } from './types' +import type { Align, NodeToolbarProps } from './types' const props = withDefaults(defineProps(), { position: Position.Top, offset: 10, + align: 'center', }) const contextNodeId = inject(NodeIdInjection, null) const { viewportRef, viewport, getSelectedNodes, findNode } = useVueFlow() -function getTransform(nodeRect: Rect, transform: ViewportTransform, position: Position, offset: number): string { - let xPos = (nodeRect.x + nodeRect.width / 2) * transform.zoom + transform.x - let yPos = nodeRect.y * transform.zoom + transform.y - offset - let xShift = -50 - let yShift = -100 +function getTransform(nodeRect: Rect, transform: ViewportTransform, position: Position, offset: number, align: Align): string { + let alignmentOffset = 0.5 + + if (align === 'start') { + alignmentOffset = 0 + } else if (align === 'end') { + alignmentOffset = 1 + } + + // position === Position.Top + // we set the x any y position of the toolbar based on the nodes position + let pos = [ + (nodeRect.x + nodeRect.width * alignmentOffset) * transform.zoom + transform.x, + nodeRect.y * transform.zoom + transform.y - offset, + ] + // and then shift it based on the alignment. The shift values are in %. + let shift = [-100 * alignmentOffset, -100] switch (position) { case Position.Right: - xPos = (nodeRect.x + nodeRect.width) * transform.zoom + transform.x + offset - yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y - xShift = 0 - yShift = -50 + pos = [ + (nodeRect.x + nodeRect.width) * transform.zoom + transform.x + offset, + (nodeRect.y + nodeRect.height * alignmentOffset) * transform.zoom + transform.y, + ] + shift = [0, -100 * alignmentOffset] break case Position.Bottom: - yPos = (nodeRect.y + nodeRect.height) * transform.zoom + transform.y + offset - yShift = 0 + pos[1] = (nodeRect.y + nodeRect.height) * transform.zoom + transform.y + offset + shift[1] = 0 break case Position.Left: - xPos = nodeRect.x * transform.zoom + transform.x - offset - yPos = (nodeRect.y + nodeRect.height / 2) * transform.zoom + transform.y - xShift = -100 - yShift = -50 + pos = [ + nodeRect.x * transform.zoom + transform.x - offset, + (nodeRect.y + nodeRect.height * alignmentOffset) * transform.zoom + transform.y, + ] + shift = [-100, -100 * alignmentOffset] break } - return `translate(${xPos}px, ${yPos}px) translate(${xShift}%, ${yShift}%)` + return `translate(${pos[0]}px, ${pos[1]}px) translate(${shift[0]}%, ${shift[1]}%)` } const nodes = computed(() => { @@ -63,13 +78,13 @@ const isActive = computed(() => : nodes.value.length === 1 && nodes.value[0].selected && getSelectedNodes.value.length === 1, ) -const nodeRect = computed(() => getRectOfNodes(nodes.value)) +const nodeRect = computed(() => getRectOfNodes(nodes.value)) -const zIndex = computed(() => Math.max(...nodes.value.map((node) => (node.computedPosition.z || 1) + 1))) +const zIndex = computed(() => Math.max(...nodes.value.map((node) => (node.computedPosition.z || 1) + 1))) const wrapperStyle = computed(() => ({ position: 'absolute', - transform: getTransform(nodeRect.value, viewport.value, props.position, props.offset), + transform: getTransform(nodeRect.value, viewport.value, props.position, props.offset, props.align), zIndex: zIndex.value, })) diff --git a/packages/node-toolbar/src/types.ts b/packages/node-toolbar/src/types.ts index b01e8796..12c416ed 100644 --- a/packages/node-toolbar/src/types.ts +++ b/packages/node-toolbar/src/types.ts @@ -5,4 +5,7 @@ export interface NodeToolbarProps { isVisible?: boolean position?: Position offset?: number + align?: 'center' | 'start' | 'end' } + +export type Align = 'center' | 'start' | 'end'