diff --git a/vue/primitives/src/canvas/flow/FlowBackground.vue b/vue/primitives/src/canvas/flow/FlowBackground.vue index dd86c77..38ede46 100644 --- a/vue/primitives/src/canvas/flow/FlowBackground.vue +++ b/vue/primitives/src/canvas/flow/FlowBackground.vue @@ -54,7 +54,7 @@ const linePath = computed(() => { diff --git a/vue/primitives/src/canvas/flow/FlowPane.vue b/vue/primitives/src/canvas/flow/FlowPane.vue index 43f3737..6bf6204 100644 --- a/vue/primitives/src/canvas/flow/FlowPane.vue +++ b/vue/primitives/src/canvas/flow/FlowPane.vue @@ -65,8 +65,10 @@ useKeyboard(currentElement, ctx, useViewportApi(ctx)); useEventListener(currentElement, 'click', (event: MouseEvent) => { const target = event.target as Element | null; - if (target && !target.closest('[data-flow-node],[data-flow-edge]')) + if (target && !target.closest('[data-flow-node],[data-flow-edge]')) { ctx.clearSelection(); + ctx.emitPaneClick(event as PointerEvent); + } }); @@ -79,7 +81,16 @@ useEventListener(currentElement, 'click', (event: MouseEvent) => { :data-interactive="ctx.interactive.value ? '' : undefined" :role="ctx.disableKeyboardA11y.value ? undefined : 'application'" :tabindex="ctx.disableKeyboardA11y.value ? undefined : 0" - :style="{ position: 'relative', overflow: 'hidden', touchAction: 'none' }" + :style="{ + position: 'relative', + overflow: 'hidden', + touchAction: 'none', + // Everything inside is absolutely positioned, so content-sizing always + // collapsed to 0×N and the graph rendered into an invisible strip. + // Vue merges a consumer's style attr over this, so it stays overridable. + width: '100%', + height: '100%', + }" > diff --git a/vue/primitives/src/canvas/flow/FlowPanel.vue b/vue/primitives/src/canvas/flow/FlowPanel.vue index 7e44c53..ca8040f 100644 --- a/vue/primitives/src/canvas/flow/FlowPanel.vue +++ b/vue/primitives/src/canvas/flow/FlowPanel.vue @@ -28,7 +28,9 @@ const { forwardRef } = useForwardExpose(); const style = computed(() => { const [v, h] = position.split('-') as ['top' | 'bottom', 'left' | 'center' | 'right']; - const s: CSSProperties = { position: 'absolute', pointerEvents: 'all' }; + // Above the viewport's explicit layer (zIndex 1): a positioned sibling + // with z-index auto would otherwise paint underneath the graph. + const s: CSSProperties = { position: 'absolute', pointerEvents: 'all', zIndex: 2 }; s[v] = '0'; if (h === 'center') { s.left = '50%'; diff --git a/vue/primitives/src/canvas/flow/FlowRoot.vue b/vue/primitives/src/canvas/flow/FlowRoot.vue index d3057da..0677829 100644 --- a/vue/primitives/src/canvas/flow/FlowRoot.vue +++ b/vue/primitives/src/canvas/flow/FlowRoot.vue @@ -73,6 +73,14 @@ export interface FlowRootProps extends PrimitiveProps { isValidConnection?: IsValidConnection; /** Cull nodes/edges outside the viewport — for large graphs. @default false */ onlyRenderVisibleElements?: boolean; + /** + * Frame the whole graph once after the initial nodes are measured. Skipped + * when an explicit `viewport` / `defaultViewport` is provided — a restored + * viewport must not be stomped by a fit. With virtualization the fit uses + * whatever is measured plus declared node sizes; fully unmeasured nodes are + * framed by position alone. @default false + */ + fitViewOnMount?: boolean | FitViewParams; /** Extra px kept rendered around the viewport when virtualizing. @default 200 */ virtualizationBuffer?: number; } @@ -87,12 +95,13 @@ export interface FlowRootEmits { selectionChange: [selection: { nodes: string[]; edges: string[] }]; paneClick: [event: PointerEvent]; nodeClick: [id: string, event: PointerEvent]; + nodeDoubleClick: [id: string, event: PointerEvent]; edgeClick: [id: string, event: PointerEvent]; }