From dfce24a5faf3da3baa32334d1de48741bb06ab1a Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Fri, 10 Feb 2023 14:08:31 +0100 Subject: [PATCH] refactor(core): use noop viewport helper when viewport is not ready Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --- packages/core/src/composables/useViewport.ts | 169 ++++++++++-------- .../core/src/container/Viewport/Transform.vue | 13 +- packages/core/src/store/actions.ts | 17 +- packages/core/src/types/store.ts | 2 +- 4 files changed, 109 insertions(+), 92 deletions(-) diff --git a/packages/core/src/composables/useViewport.ts b/packages/core/src/composables/useViewport.ts index 5b1d3a16..6d17e7ba 100644 --- a/packages/core/src/composables/useViewport.ts +++ b/packages/core/src/composables/useViewport.ts @@ -3,81 +3,26 @@ import type { ComputedGetters, D3Selection, GraphNode, State, ViewportFunctions const DEFAULT_PADDING = 0.1 -export default (state: State, getters: ComputedGetters): ViewportFunctions => { +const noop = () => {} + +const initialViewportHelper: ViewportFunctions = { + zoomIn: noop, + zoomOut: noop, + zoomTo: noop, + fitView: noop, + setCenter: noop, + fitBounds: noop, + project: (position) => position, + setTransform: noop, + getTransform: () => ({ x: 0, y: 0, zoom: 1 }), +} + +export default (state: State, getters: ComputedGetters) => { const { nodes, d3Zoom, d3Selection, dimensions, translateExtent, minZoom, maxZoom, viewport, snapToGrid, snapGrid } = $(state) const { getNodes } = $(getters) - return { - zoomIn: async (options) => { - await zoom(1.2, options?.duration) - }, - zoomOut: async (options) => { - await zoom(1 / 1.2, options?.duration) - }, - zoomTo: async (zoomLevel, options) => { - if (d3Selection && d3Zoom) { - d3Zoom.scaleTo(transition(d3Selection, options?.duration), zoomLevel) - } - }, - setTransform: async (transform, options) => { - transformViewport(transform.x, transform.y, transform.zoom, options?.duration) - }, - getTransform: () => ({ - x: viewport.x, - y: viewport.y, - zoom: viewport.zoom, - }), - fitView: async ( - options = { - padding: DEFAULT_PADDING, - includeHiddenNodes: false, - duration: 0, - }, - ) => { - if (!nodes.length) return - - const nodesToFit: GraphNode[] = (options.includeHiddenNodes ? nodes : getNodes).filter((node) => { - const initialized = node.initialized && node.dimensions.width && node.dimensions.height - let shouldInclude = true - - if (options.nodes?.length) { - shouldInclude = options.nodes.includes(node.id) - } - - return initialized && shouldInclude - }) - - const bounds = getRectOfNodes(nodesToFit) - - const { x, y, zoom } = getTransformForBounds( - bounds, - dimensions.width, - dimensions.height, - options.minZoom ?? minZoom, - options.maxZoom ?? maxZoom, - options.padding ?? DEFAULT_PADDING, - options.offset, - ) - - transformViewport(x, y, zoom, options?.duration) - }, - setCenter: async (x, y, options) => { - const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom - const centerX = dimensions.width / 2 - x * nextZoom - const centerY = dimensions.height / 2 - y * nextZoom - - transformViewport(centerX, centerY, nextZoom, options?.duration) - }, - fitBounds: async (bounds, options = { padding: DEFAULT_PADDING }) => { - const { x, y, zoom } = getTransformForBounds(bounds, dimensions.width, dimensions.height, minZoom, maxZoom, options.padding) - - transformViewport(x, y, zoom, options?.duration) - }, - project: (position) => pointToRendererPoint(position, viewport, snapToGrid, snapGrid), - } - - async function zoom(scale: number, duration?: number) { + function zoom(scale: number, duration?: number) { if (d3Selection && d3Zoom) { d3Zoom.scaleBy(transition(d3Selection, duration), scale) } @@ -93,6 +38,88 @@ export default (state: State, getters: ComputedGetters): ViewportFunctions => { d3Zoom.transform(transition(d3Selection, duration), nextTransform) } } + + return computed(() => { + if (d3Zoom && d3Selection) { + return { + zoomIn: (options) => { + zoom(1.2, options?.duration) + }, + zoomOut: (options) => { + zoom(1 / 1.2, options?.duration) + }, + zoomTo: (zoomLevel, options) => { + if (d3Selection && d3Zoom) { + d3Zoom.scaleTo(transition(d3Selection, options?.duration), zoomLevel) + } + }, + setTransform: (transform, options) => { + transformViewport(transform.x, transform.y, transform.zoom, options?.duration) + }, + getTransform: () => ({ + x: viewport.x, + y: viewport.y, + zoom: viewport.zoom, + }), + fitView: ( + options = { + padding: DEFAULT_PADDING, + includeHiddenNodes: false, + duration: 0, + }, + ) => { + if (!nodes.length) return + + const nodesToFit: GraphNode[] = (options.includeHiddenNodes ? nodes : getNodes).filter((node) => { + const initialized = node.initialized && node.dimensions.width && node.dimensions.height + let shouldInclude = true + + if (options.nodes?.length) { + shouldInclude = options.nodes.includes(node.id) + } + + return initialized && shouldInclude + }) + + const bounds = getRectOfNodes(nodesToFit) + + const { x, y, zoom } = getTransformForBounds( + bounds, + dimensions.width, + dimensions.height, + options.minZoom ?? minZoom, + options.maxZoom ?? maxZoom, + options.padding ?? DEFAULT_PADDING, + options.offset, + ) + + transformViewport(x, y, zoom, options?.duration) + }, + setCenter: (x, y, options) => { + const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom + const centerX = dimensions.width / 2 - x * nextZoom + const centerY = dimensions.height / 2 - y * nextZoom + + transformViewport(centerX, centerY, nextZoom, options?.duration) + }, + fitBounds: (bounds, options = { padding: DEFAULT_PADDING }) => { + const { x, y, zoom } = getTransformForBounds( + bounds, + dimensions.width, + dimensions.height, + minZoom, + maxZoom, + options.padding, + ) + + transformViewport(x, y, zoom, options?.duration) + }, + project: (position) => pointToRendererPoint(position, viewport, snapToGrid, snapGrid), + } + } + + return initialViewportHelper + }) } function transition(selection: D3Selection, ms = 0) { diff --git a/packages/core/src/container/Viewport/Transform.vue b/packages/core/src/container/Viewport/Transform.vue index 00a0328d..d25069a6 100644 --- a/packages/core/src/container/Viewport/Transform.vue +++ b/packages/core/src/container/Viewport/Transform.vue @@ -2,26 +2,21 @@ import NodeRenderer from '../NodeRenderer/NodeRenderer.vue' import EdgeRenderer from '../EdgeRenderer/EdgeRenderer.vue' -const { id, viewport, emits, onNodesInitialized, ...rest } = useVueFlow() +const { id, viewport, emits, ...rest } = useVueFlow() let isReady = $ref(false) -onNodesInitialized(() => { - setTimeout(() => { - // hide graph until nodes are ready, so we don't have jumping nodes - isReady = true - }, 0) -}) - onMounted(async () => { setTimeout(() => { emits.paneReady({ id, viewport, emits, - onNodesInitialized, ...rest, }) + + // hide graph until nodes are ready, so we don't have jumping nodes + isReady = true }, 1) }) diff --git a/packages/core/src/store/actions.ts b/packages/core/src/store/actions.ts index e89676c8..b8514dbf 100644 --- a/packages/core/src/store/actions.ts +++ b/packages/core/src/store/actions.ts @@ -23,15 +23,13 @@ import type { export function useActions(state: State, getters: ComputedGetters): Actions { let fitViewOnInitDone = false - const updateNodeInternals: Actions['updateNodeInternals'] = (ids) => { - state.hooks.updateNodeInternals.trigger(ids) - } - - const viewportHelper = useViewport(state, getters) + const viewportHelper = $(useViewport(state, getters)) const nodeIds = $computed(() => state.nodes.map((n) => n.id)) const edgeIds = $computed(() => state.edges.map((e) => e.id)) + const updateNodeInternals: Actions['updateNodeInternals'] = (ids) => state.hooks.updateNodeInternals.trigger(ids ?? nodeIds) + const findNode: Actions['findNode'] = (id) => { if (state.nodes && !nodeIds.length) return state.nodes.find((node) => node.id === id) @@ -83,6 +81,7 @@ export function useActions(state: State, getters: ComputedGetters): Actions { if (!viewportNode) return + // todo: remove this feature again, it's not working properly let zoom: number if (state.__experimentalFeatures?.nestedFlow) { let viewportNodes: HTMLElement[] = [viewportNode] @@ -625,18 +624,14 @@ export function useActions(state: State, getters: ComputedGetters): Actions { setTransform: (transform, options) => { viewportHelper.setTransform(transform, options) }, - getTransform: () => ({ - x: state.viewport.x, - y: state.viewport.y, - zoom: state.viewport.zoom, - }), + getTransform: () => viewportHelper.getTransform(), setCenter: (x, y, options) => { viewportHelper.setCenter(x, y, options) }, fitBounds: (bounds, options) => { viewportHelper.fitBounds(bounds, options) }, - project: (position) => pointToRendererPoint(position, state.viewport, state.snapToGrid, state.snapGrid), + project: (position) => viewportHelper.project(position), toObject, updateNodeInternals, $reset: () => { diff --git a/packages/core/src/types/store.ts b/packages/core/src/types/store.ts index 9bb6be2a..a9bc697b 100644 --- a/packages/core/src/types/store.ts +++ b/packages/core/src/types/store.ts @@ -174,7 +174,7 @@ export type UpdateNodePosition = (dragItems: NodeDragItem[], changed: boolean, d export type UpdateNodeDimensions = (updates: UpdateNodeDimensionsParams[]) => void -export type UpdateNodeInternals = (nodeIds: string[]) => void +export type UpdateNodeInternals = (nodeIds?: string[]) => void export type FindNode = = any>( id: string,