mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
feat(editor): Show minimap only while panning, zooming or while minimap is hovered (no-changelog) (#10677)
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Benchmark Docker Image CI / build (push) Waiting to run
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Benchmark Docker Image CI / build (push) Waiting to run
This commit is contained in:
parent
c5bc8e6eb9
commit
3ea114129b
|
@ -28,6 +28,7 @@ beforeEach(() => {
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
vi.useRealTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Canvas', () => {
|
describe('Canvas', () => {
|
||||||
|
@ -36,8 +37,8 @@ describe('Canvas', () => {
|
||||||
|
|
||||||
expect(getByTestId('canvas')).toBeVisible();
|
expect(getByTestId('canvas')).toBeVisible();
|
||||||
expect(getByTestId('canvas-background')).toBeVisible();
|
expect(getByTestId('canvas-background')).toBeVisible();
|
||||||
expect(getByTestId('canvas-minimap')).toBeVisible();
|
|
||||||
expect(getByTestId('canvas-controls')).toBeVisible();
|
expect(getByTestId('canvas-controls')).toBeVisible();
|
||||||
|
expect(getByTestId('canvas-minimap')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render nodes and edges', async () => {
|
it('should render nodes and edges', async () => {
|
||||||
|
@ -142,4 +143,79 @@ describe('Canvas', () => {
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('minimap', () => {
|
||||||
|
const minimapVisibilityDelay = 1000;
|
||||||
|
const minimapTransitionDuration = 300;
|
||||||
|
|
||||||
|
it('should show minimap for 1sec after panning', async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
|
||||||
|
const nodes = [createCanvasNodeElement()];
|
||||||
|
const { getByTestId, container } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => expect(container.querySelectorAll('.vue-flow__node')).toHaveLength(1));
|
||||||
|
|
||||||
|
const canvas = getByTestId('canvas');
|
||||||
|
const pane = canvas.querySelector('.vue-flow__pane');
|
||||||
|
if (!pane) throw new Error('VueFlow pane not found');
|
||||||
|
|
||||||
|
await fireEvent.keyDown(pane, { view: window, key: 'Shift' });
|
||||||
|
await fireEvent.mouseDown(pane, { view: window });
|
||||||
|
await fireEvent.mouseMove(pane, {
|
||||||
|
view: window,
|
||||||
|
clientX: 100,
|
||||||
|
clientY: 100,
|
||||||
|
});
|
||||||
|
await fireEvent.mouseUp(pane, { view: window });
|
||||||
|
await fireEvent.keyUp(pane, { view: window, key: 'Shift' });
|
||||||
|
|
||||||
|
vi.advanceTimersByTime(minimapTransitionDuration);
|
||||||
|
await waitFor(() => expect(getByTestId('canvas-minimap')).toBeVisible());
|
||||||
|
vi.advanceTimersByTime(minimapVisibilityDelay + minimapTransitionDuration);
|
||||||
|
await waitFor(() => expect(getByTestId('canvas-minimap')).not.toBeVisible());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep minimap visible when hovered', async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
|
||||||
|
const nodes = [createCanvasNodeElement()];
|
||||||
|
const { getByTestId, container } = renderComponent({
|
||||||
|
props: {
|
||||||
|
nodes,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => expect(container.querySelectorAll('.vue-flow__node')).toHaveLength(1));
|
||||||
|
|
||||||
|
const canvas = getByTestId('canvas');
|
||||||
|
const pane = canvas.querySelector('.vue-flow__pane');
|
||||||
|
if (!pane) throw new Error('VueFlow pane not found');
|
||||||
|
|
||||||
|
await fireEvent.keyDown(pane, { view: window, key: 'Shift' });
|
||||||
|
await fireEvent.mouseDown(pane, { view: window });
|
||||||
|
await fireEvent.mouseMove(pane, {
|
||||||
|
view: window,
|
||||||
|
clientX: 100,
|
||||||
|
clientY: 100,
|
||||||
|
});
|
||||||
|
await fireEvent.mouseUp(pane, { view: window });
|
||||||
|
await fireEvent.keyUp(pane, { view: window, key: 'Shift' });
|
||||||
|
|
||||||
|
vi.advanceTimersByTime(minimapTransitionDuration);
|
||||||
|
await waitFor(() => expect(getByTestId('canvas-minimap')).toBeVisible());
|
||||||
|
|
||||||
|
await fireEvent.mouseEnter(getByTestId('canvas-minimap'));
|
||||||
|
vi.advanceTimersByTime(minimapVisibilityDelay + minimapTransitionDuration);
|
||||||
|
await waitFor(() => expect(getByTestId('canvas-minimap')).toBeVisible());
|
||||||
|
|
||||||
|
await fireEvent.mouseLeave(getByTestId('canvas-minimap'));
|
||||||
|
vi.advanceTimersByTime(minimapVisibilityDelay + minimapTransitionDuration);
|
||||||
|
await waitFor(() => expect(getByTestId('canvas-minimap')).not.toBeVisible());
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -309,6 +309,7 @@ function emitWithLastSelectedNode(emitFn: (id: string) => void) {
|
||||||
|
|
||||||
const defaultZoom = 1;
|
const defaultZoom = 1;
|
||||||
const zoom = ref(defaultZoom);
|
const zoom = ref(defaultZoom);
|
||||||
|
const isPaneMoving = ref(false);
|
||||||
|
|
||||||
function getProjectedPosition(event?: MouseEvent) {
|
function getProjectedPosition(event?: MouseEvent) {
|
||||||
const bounds = viewportRef.value?.getBoundingClientRect() ?? { left: 0, top: 0 };
|
const bounds = viewportRef.value?.getBoundingClientRect() ?? { left: 0, top: 0 };
|
||||||
|
@ -354,6 +355,14 @@ function setReadonly(value: boolean) {
|
||||||
elementsSelectable.value = true;
|
elementsSelectable.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPaneMoveStart() {
|
||||||
|
isPaneMoving.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPaneMoveEnd() {
|
||||||
|
isPaneMoving.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context menu
|
* Context menu
|
||||||
*/
|
*/
|
||||||
|
@ -414,10 +423,45 @@ function onContextMenuAction(action: ContextMenuAction, nodeIds: string[]) {
|
||||||
* Minimap
|
* Minimap
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const minimapVisibilityDelay = 1000;
|
||||||
|
const minimapHideTimeout = ref<NodeJS.Timeout | null>(null);
|
||||||
|
const isMinimapVisible = ref(false);
|
||||||
|
|
||||||
function minimapNodeClassnameFn(node: CanvasNode) {
|
function minimapNodeClassnameFn(node: CanvasNode) {
|
||||||
return `minimap-node-${node.data?.render.type.replace(/\./g, '-') ?? 'default'}`;
|
return `minimap-node-${node.data?.render.type.replace(/\./g, '-') ?? 'default'}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(isPaneMoving, (value) => {
|
||||||
|
if (value) {
|
||||||
|
showMinimap();
|
||||||
|
} else {
|
||||||
|
hideMinimap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function showMinimap() {
|
||||||
|
if (minimapHideTimeout.value) {
|
||||||
|
clearTimeout(minimapHideTimeout.value);
|
||||||
|
minimapHideTimeout.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
isMinimapVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideMinimap() {
|
||||||
|
minimapHideTimeout.value = setTimeout(() => {
|
||||||
|
isMinimapVisible.value = false;
|
||||||
|
}, minimapVisibilityDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMinimapMouseEnter() {
|
||||||
|
showMinimap();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMinimapMouseLeave() {
|
||||||
|
hideMinimap();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lifecycle
|
* Lifecycle
|
||||||
*/
|
*/
|
||||||
|
@ -474,6 +518,8 @@ provide(CanvasKey, {
|
||||||
@contextmenu="onOpenContextMenu"
|
@contextmenu="onOpenContextMenu"
|
||||||
@viewport-change="onViewportChange"
|
@viewport-change="onViewportChange"
|
||||||
@nodes-change="onNodesChange"
|
@nodes-change="onNodesChange"
|
||||||
|
@move-start="onPaneMoveStart"
|
||||||
|
@move-end="onPaneMoveEnd"
|
||||||
>
|
>
|
||||||
<template #node-canvas-node="canvasNodeProps">
|
<template #node-canvas-node="canvasNodeProps">
|
||||||
<Node
|
<Node
|
||||||
|
@ -508,18 +554,23 @@ provide(CanvasKey, {
|
||||||
|
|
||||||
<Background data-test-id="canvas-background" pattern-color="#aaa" :gap="GRID_SIZE" />
|
<Background data-test-id="canvas-background" pattern-color="#aaa" :gap="GRID_SIZE" />
|
||||||
|
|
||||||
<MiniMap
|
<Transition name="minimap">
|
||||||
data-test-id="canvas-minimap"
|
<MiniMap
|
||||||
aria-label="n8n Minimap"
|
v-show="isMinimapVisible"
|
||||||
:height="120"
|
data-test-id="canvas-minimap"
|
||||||
:width="200"
|
aria-label="n8n Minimap"
|
||||||
:position="PanelPosition.BottomLeft"
|
:height="120"
|
||||||
pannable
|
:width="200"
|
||||||
zoomable
|
:position="PanelPosition.BottomLeft"
|
||||||
:node-class-name="minimapNodeClassnameFn"
|
pannable
|
||||||
mask-color="var(--color-background-base)"
|
zoomable
|
||||||
:node-border-radius="16"
|
:node-class-name="minimapNodeClassnameFn"
|
||||||
/>
|
mask-color="var(--color-background-base)"
|
||||||
|
:node-border-radius="16"
|
||||||
|
@mouseenter="onMinimapMouseEnter"
|
||||||
|
@mouseleave="onMinimapMouseLeave"
|
||||||
|
/>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
<CanvasControlButtons
|
<CanvasControlButtons
|
||||||
data-test-id="canvas-controls"
|
data-test-id="canvas-controls"
|
||||||
|
@ -556,3 +607,15 @@ provide(CanvasKey, {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.minimap-enter-active,
|
||||||
|
.minimap-leave-active {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minimap-enter-from,
|
||||||
|
.minimap-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue