Skip to content

Commit

Permalink
[Dashboard][Collapsable Panels] Add auto-scroll for dragging + resizi…
Browse files Browse the repository at this point in the history
…ng events (elastic#201867)

Closes elastic#201626

## Summary

This PR makes it so that the grid layout will auto-scroll with the
following behaviour:

- Auto-scroll up when....
- the panel is dragged to the top 5% of the window **or above**
(including outside of the window)
- Auto-scroll down when....
- the panel is dragged to the bottom 5% of the window **or below**
(including outside of the window)
- the panel is being dragged and the resize handle is dragged to the
bottom 5% of the window **or below** (including outside of the window)




https://github.com/user-attachments/assets/6880fd14-e492-4cd9-81c6-3dfb30b80960



### Checklist

- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Identify risks

There are no risks to this PR, since all work is contained in the
`examples` plugin.
  • Loading branch information
Heenawter authored Nov 27, 2024
1 parent d760144 commit d811284
Showing 1 changed file with 45 additions and 1 deletion.
46 changes: 45 additions & 1 deletion packages/kbn-grid-layout/grid/use_grid_layout_events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,49 @@ import { resolveGridRow } from './utils/resolve_grid_row';
import { GridPanelData, GridLayoutStateManager } from './types';
import { isGridDataEqual } from './utils/equality_checks';

const scrollOnInterval = (direction: 'up' | 'down') => {
let count = 0;
const interval = setInterval(() => {
// calculate the speed based on how long the interval has been going to create an ease effect
// via the parabola formula `y = a(x - h)^2 + k`
// - the starting speed is k = 50
// - the maximum speed is 250
// - the rate at which the speed increases is controlled by a = 0.75
const speed = Math.min(0.75 * count ** 2 + 50, 250);
window.scrollBy({ top: direction === 'down' ? speed : -speed, behavior: 'smooth' });
count++;
}, 100);
return interval;
};

export const useGridLayoutEvents = ({
gridLayoutStateManager,
}: {
gridLayoutStateManager: GridLayoutStateManager;
}) => {
const mouseClientPosition = useRef({ x: 0, y: 0 });
const lastRequestedPanelPosition = useRef<GridPanelData | undefined>(undefined);
const scrollInterval = useRef<NodeJS.Timeout | null>(null);

// -----------------------------------------------------------------------------------------
// Set up drag events
// -----------------------------------------------------------------------------------------
useEffect(() => {
const { runtimeSettings$, interactionEvent$, gridLayout$ } = gridLayoutStateManager;

const stopAutoScrollIfNecessary = () => {
if (scrollInterval.current) {
clearInterval(scrollInterval.current);
scrollInterval.current = null;
}
};

const calculateUserEvent = (e: Event) => {
if (!interactionEvent$.value) return;
if (!interactionEvent$.value) {
// if no interaction event, stop auto scroll (if necessary) and return early
stopAutoScrollIfNecessary();
return;
}
e.preventDefault();
e.stopPropagation();

Expand Down Expand Up @@ -122,6 +150,20 @@ export const useGridLayoutEvents = ({
requestedGridData.row = targetRow;
}

// auto scroll when an event is happening close to the top or bottom of the screen
const heightPercentage =
100 - ((window.innerHeight - mouseTargetPixel.y) / window.innerHeight) * 100;
const startScrollingUp = !isResize && heightPercentage < 5; // don't scroll up when resizing
const startScrollingDown = heightPercentage > 95;
if (startScrollingUp || startScrollingDown) {
if (!scrollInterval.current) {
// only start scrolling if it's not already happening
scrollInterval.current = scrollOnInterval(startScrollingUp ? 'up' : 'down');
}
} else {
stopAutoScrollIfNecessary();
}

// resolve the new grid layout
if (
hasChangedGridRow ||
Expand Down Expand Up @@ -153,6 +195,8 @@ export const useGridLayoutEvents = ({
};

const onMouseMove = (e: MouseEvent) => {
// Note: When an item is being interacted with, `mousemove` events continue to be fired, even when the
// mouse moves out of the window (i.e. when a panel is being dragged around outside the window).
mouseClientPosition.current = { x: e.clientX, y: e.clientY };
calculateUserEvent(e);
};
Expand Down

0 comments on commit d811284

Please sign in to comment.