fix: custom node example not fitting view
* fit view can't work (for the onLoad event) for elements assigned after the host has mounted (onMounted hook) as we can't calculate a rect of elements that don't exist at flow creation * in this case just trigger fit view __after__ elements have been created/passed to the flow Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -43,60 +43,58 @@ const nodeColor = (n: Node): string => {
|
|||||||
return '#fff'
|
return '#fff'
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
const onChange = (event: Event) => {
|
||||||
const onChange = (event: Event) => {
|
elements.value = elements.value.map((e: FlowElement) => {
|
||||||
elements.value = elements.value.map((e: FlowElement) => {
|
if (isEdge(e) || e.id !== '2') {
|
||||||
if (isEdge(e) || e.id !== '2') {
|
return e
|
||||||
return e
|
}
|
||||||
}
|
const color = (event.target as HTMLInputElement).value
|
||||||
const color = (event.target as HTMLInputElement).value
|
bgColor.value = color
|
||||||
bgColor.value = color
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...e,
|
...e,
|
||||||
data: {
|
data: {
|
||||||
...e.data,
|
...e.data,
|
||||||
color,
|
color,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.value = [
|
elements.value = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
data: { label: 'An input node' },
|
data: { label: 'An input node' },
|
||||||
position: { x: 0, y: 50 },
|
position: { x: 0, y: 50 },
|
||||||
sourcePosition: Position.Right,
|
sourcePosition: Position.Right,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'selectorNode',
|
type: 'selectorNode',
|
||||||
data: { onChange, color: bgColor.value },
|
data: { onChange, color: bgColor.value },
|
||||||
style: { border: '1px solid #777', padding: '10px' },
|
style: { border: '1px solid #777', padding: '10px' },
|
||||||
position: { x: 250, y: 50 },
|
position: { x: 250, y: 50 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
type: 'output',
|
type: 'output',
|
||||||
data: { label: 'Output A' },
|
data: { label: 'Output A' },
|
||||||
position: { x: 650, y: 25 },
|
position: { x: 650, y: 25 },
|
||||||
targetPosition: Position.Left,
|
targetPosition: Position.Left,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
type: 'output',
|
type: 'output',
|
||||||
data: { label: 'Output B' },
|
data: { label: 'Output B' },
|
||||||
position: { x: 650, y: 100 },
|
position: { x: 650, y: 100 },
|
||||||
targetPosition: Position.Left,
|
targetPosition: Position.Left,
|
||||||
},
|
},
|
||||||
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#fff' } },
|
{ id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#fff' } },
|
||||||
{ id: 'e2a-3', source: '2', sourceHandle: 'a', target: '3', animated: true, style: { stroke: '#fff' } },
|
{ id: 'e2a-3', source: '2', sourceHandle: 'a', target: '3', animated: true, style: { stroke: '#fff' } },
|
||||||
{ id: 'e2b-4', source: '2', sourceHandle: 'b', target: '4', animated: true, style: { stroke: '#fff' } },
|
{ id: 'e2b-4', source: '2', sourceHandle: 'b', target: '4', animated: true, style: { stroke: '#fff' } },
|
||||||
]
|
]
|
||||||
})
|
|
||||||
|
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ const elements = ref<Elements>(initialElements)
|
|||||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge({ ...params, animated: true }, elements.value))
|
const onConnect = (params: Connection | Edge) => (elements.value = addEdge({ ...params, animated: true }, elements.value))
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
||||||
|
|
||||||
// todo changing elements not properly updating flowchart...
|
|
||||||
const onLayout = (direction: string) => {
|
const onLayout = (direction: string) => {
|
||||||
const isHorizontal = direction === 'LR'
|
const isHorizontal = direction === 'LR'
|
||||||
dagreGraph.setGraph({ rankdir: direction })
|
dagreGraph.setGraph({ rankdir: direction })
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ export default (store = useStore()): UseZoomPanHelper => {
|
|||||||
bounds,
|
bounds,
|
||||||
store.dimensions.width,
|
store.dimensions.width,
|
||||||
store.dimensions.height,
|
store.dimensions.height,
|
||||||
options.minZoom || store.minZoom,
|
options.minZoom ?? store.minZoom,
|
||||||
options.maxZoom || store.maxZoom,
|
options.maxZoom ?? store.maxZoom,
|
||||||
options.padding || DEFAULT_PADDING,
|
options.padding ?? DEFAULT_PADDING,
|
||||||
)
|
)
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom)
|
const transform = zoomIdentity.translate(x, y).scale(zoom)
|
||||||
|
|
||||||
|
|||||||
@@ -69,122 +69,114 @@ until(zoomPaneEl)
|
|||||||
store.initD3Zoom({ d3Zoom: d3z, d3Selection: d3s, d3ZoomHandler })
|
store.initD3Zoom({ d3Zoom: d3z, d3Selection: d3s, d3ZoomHandler })
|
||||||
store.transform = [updatedTransform.x, updatedTransform.y, updatedTransform.k]
|
store.transform = [updatedTransform.x, updatedTransform.y, updatedTransform.k]
|
||||||
|
|
||||||
const applyZoomHandlers = () => {
|
d3z.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
d3z.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
if (viewChanged(transform.value, event.transform)) {
|
||||||
if (viewChanged(transform.value, event.transform)) {
|
const flowTransform = eventToFlowTransform(event.transform)
|
||||||
const flowTransform = eventToFlowTransform(event.transform)
|
transform.value = flowTransform
|
||||||
transform.value = flowTransform
|
|
||||||
|
|
||||||
hooks.moveStart.trigger(flowTransform)
|
hooks.moveStart.trigger(flowTransform)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
d3z.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3z.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
if (viewChanged(transform.value, event.transform)) {
|
if (viewChanged(transform.value, event.transform)) {
|
||||||
const flowTransform = eventToFlowTransform(event.transform)
|
const flowTransform = eventToFlowTransform(event.transform)
|
||||||
transform.value = flowTransform
|
transform.value = flowTransform
|
||||||
|
|
||||||
hooks.moveEnd.trigger(flowTransform)
|
hooks.moveEnd.trigger(flowTransform)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useKeyPress(props.selectionKeyCode, (keyPress) => {
|
useKeyPress(props.selectionKeyCode, (keyPress) => {
|
||||||
if (keyPress) {
|
if (keyPress) {
|
||||||
d3z.on('zoom', null)
|
d3z.on('zoom', null)
|
||||||
} else {
|
} else {
|
||||||
d3z.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3z.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
if (viewChanged(transform.value, event.transform)) {
|
if (viewChanged(transform.value, event.transform)) {
|
||||||
const flowTransform = eventToFlowTransform(event.transform)
|
const flowTransform = eventToFlowTransform(event.transform)
|
||||||
transform.value = flowTransform
|
transform.value = flowTransform
|
||||||
|
|
||||||
hooks.move.trigger(flowTransform)
|
hooks.move.trigger(flowTransform)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
useKeyPress(props.zoomActivationKeyCode, (keyPress) => {
|
||||||
|
if (props.panOnScroll && keyPress) {
|
||||||
|
d3s
|
||||||
|
?.on('wheel', (event: WheelEvent) => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopImmediatePropagation()
|
||||||
|
|
||||||
|
const currentZoom = d3s?.property('__zoom').k || 1
|
||||||
|
|
||||||
|
if (event.ctrlKey && props.zoomOnPinch) {
|
||||||
|
const point = pointer(event)
|
||||||
|
// taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
|
||||||
|
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10
|
||||||
|
const zoom = currentZoom * Math.pow(2, pinchDelta)
|
||||||
|
if (d3s) d3z.scaleTo(d3s, zoom, point)
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// increase scroll speed in firefox
|
||||||
|
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
||||||
|
const deltaNormalize = event.deltaMode === 1 ? 20 : 1
|
||||||
|
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
|
||||||
|
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
|
||||||
|
|
||||||
|
if (d3s && props.panOnScrollSpeed)
|
||||||
|
d3z?.translateBy(
|
||||||
|
d3s,
|
||||||
|
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
||||||
|
-(deltaY / currentZoom) * props.panOnScrollSpeed,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
.on('wheel.zoom', null)
|
||||||
})
|
} else if (typeof d3ZoomHandler !== 'undefined') {
|
||||||
|
d3s?.on('wheel', null).on('wheel.zoom', d3ZoomHandler)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
useKeyPress(props.zoomActivationKeyCode, (keyPress) => {
|
const keyPress = useKeyPress(props.selectionKeyCode)
|
||||||
if (props.panOnScroll && keyPress) {
|
d3z.filter((event: MouseEvent) => {
|
||||||
d3s
|
const zoomScroll = props.zoomOnScroll
|
||||||
?.on('wheel', (event: WheelEvent) => {
|
const pinchZoom = props.zoomOnPinch && event.ctrlKey
|
||||||
event.preventDefault()
|
|
||||||
event.stopImmediatePropagation()
|
|
||||||
|
|
||||||
const currentZoom = d3s?.property('__zoom').k || 1
|
// if all interactions are disabled, we prevent all zoom events
|
||||||
|
if (!props.paneMoveable && !zoomScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch) return false
|
||||||
|
|
||||||
if (event.ctrlKey && props.zoomOnPinch) {
|
// during a selection we prevent all other interactions
|
||||||
const point = pointer(event)
|
if (keyPress.value) return false
|
||||||
// taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
|
|
||||||
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10
|
|
||||||
const zoom = currentZoom * Math.pow(2, pinchDelta)
|
|
||||||
if (d3s) d3z.scaleTo(d3s, zoom, point)
|
|
||||||
|
|
||||||
return
|
// if zoom on double click is disabled, we prevent the double click event
|
||||||
}
|
if (!props.zoomOnDoubleClick && event.type === 'dblclick') return false
|
||||||
|
|
||||||
// increase scroll speed in firefox
|
if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') return false
|
||||||
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
|
||||||
const deltaNormalize = event.deltaMode === 1 ? 20 : 1
|
|
||||||
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
|
|
||||||
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
|
|
||||||
|
|
||||||
if (d3s && props.panOnScrollSpeed)
|
// when the target element is a node, we still allow zooming
|
||||||
d3z?.translateBy(
|
if (
|
||||||
d3s,
|
((event.target as Element).closest('.vue-flow__node') || (event.target as Element).closest('.vue-flow__edge')) &&
|
||||||
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
event.type !== 'wheel'
|
||||||
-(deltaY / currentZoom) * props.panOnScrollSpeed,
|
)
|
||||||
)
|
return false
|
||||||
})
|
|
||||||
.on('wheel.zoom', null)
|
|
||||||
} else if (typeof d3ZoomHandler !== 'undefined') {
|
|
||||||
d3s?.on('wheel', null).on('wheel.zoom', d3ZoomHandler)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const applyZoomFilter = () => {
|
// when the target element is a node selection, we still allow zooming
|
||||||
const keyPress = useKeyPress(props.selectionKeyCode)
|
if ((event.target as Element).closest('.vue-flow__nodesselection') && event.type !== 'wheel') return false
|
||||||
d3z.filter((event: MouseEvent) => {
|
|
||||||
const zoomScroll = props.zoomOnScroll
|
|
||||||
const pinchZoom = props.zoomOnPinch && event.ctrlKey
|
|
||||||
|
|
||||||
// if all interactions are disabled, we prevent all zoom events
|
if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') return false
|
||||||
if (!props.paneMoveable && !zoomScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch)
|
|
||||||
return false
|
|
||||||
|
|
||||||
// during a selection we prevent all other interactions
|
// when there is no scroll handling enabled, we prevent all wheel events
|
||||||
if (keyPress.value) return false
|
if (!zoomScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') return false
|
||||||
|
|
||||||
// if zoom on double click is disabled, we prevent the double click event
|
// if the pane is not movable, we prevent dragging it with mousestart or touchstart
|
||||||
if (!props.zoomOnDoubleClick && event.type === 'dblclick') return false
|
if (!props.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) return false
|
||||||
|
|
||||||
if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') return false
|
// default filter for d3-zoom
|
||||||
|
return (!event.ctrlKey || event.type === 'wheel') && !event.button
|
||||||
// when the target element is a node, we still allow zooming
|
})
|
||||||
if (
|
|
||||||
((event.target as Element).closest('.vue-flow__node') || (event.target as Element).closest('.vue-flow__edge')) &&
|
|
||||||
event.type !== 'wheel'
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
|
|
||||||
// when the target element is a node selection, we still allow zooming
|
|
||||||
if ((event.target as Element).closest('.vue-flow__nodesselection') && event.type !== 'wheel') return false
|
|
||||||
|
|
||||||
if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') return false
|
|
||||||
|
|
||||||
// when there is no scroll handling enabled, we prevent all wheel events
|
|
||||||
if (!zoomScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') return false
|
|
||||||
|
|
||||||
// if the pane is not movable, we prevent dragging it with mousestart or touchstart
|
|
||||||
if (!props.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) return false
|
|
||||||
|
|
||||||
// default filter for d3-zoom
|
|
||||||
return (!event.ctrlKey || event.type === 'wheel') && !event.button
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
applyZoomHandlers()
|
|
||||||
applyZoomFilter()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const { width, height } = useElementBounding(zoomPaneEl)
|
const { width, height } = useElementBounding(zoomPaneEl)
|
||||||
@@ -197,9 +189,8 @@ watch(width, (val) => (store.dimensions.width = val))
|
|||||||
watch(height, (val) => (store.dimensions.height = val))
|
watch(height, (val) => (store.dimensions.height = val))
|
||||||
watch(transform, (val) => (store.transform = [val.x, val.y, val.zoom]))
|
watch(transform, (val) => (store.transform = [val.x, val.y, val.zoom]))
|
||||||
|
|
||||||
|
const { zoomIn, zoomOut, zoomTo, transform: setTransform, fitView } = useZoomPanHelper()
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const { zoomIn, zoomOut, zoomTo, transform: setTransform, fitView } = useZoomPanHelper()
|
|
||||||
|
|
||||||
watchOnce(
|
watchOnce(
|
||||||
() => width.value && height.value,
|
() => width.value && height.value,
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
+1
-1
@@ -222,7 +222,7 @@ export const getRectOfNodes = (nodes: Node[]): Rect => {
|
|||||||
...position,
|
...position,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
} as any),
|
}),
|
||||||
),
|
),
|
||||||
{ x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity },
|
{ x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity },
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user