mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-19 10:56:04 +08:00
dbeaver/pro#9659 add dnd scroll container (#4476)
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { forwardRef, useEffect, useRef } from 'react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
import { useMergeRefs, useMouse } from '@cloudbeaver/core-blocks';
|
||||
|
||||
const EDGE = 60;
|
||||
const MAX_SPEED = 12;
|
||||
|
||||
export interface IDNDScrollContainerProps {
|
||||
isDragging: boolean;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DNDScrollContainer = observer(
|
||||
forwardRef<HTMLDivElement, IDNDScrollContainerProps>(function DNDScrollContainer({ isDragging, className, children }, externalRef) {
|
||||
const innerRef = useRef<HTMLDivElement>(null);
|
||||
const speedRef = useRef(0);
|
||||
const rafRef = useRef<number>(null);
|
||||
|
||||
const mouse = useMouse<HTMLDivElement>();
|
||||
|
||||
const el = innerRef.current;
|
||||
|
||||
if (isDragging && el && mouse.state.position) {
|
||||
const y = mouse.state.position.y;
|
||||
const h = el.getBoundingClientRect().height;
|
||||
|
||||
if (y < EDGE) {
|
||||
speedRef.current = -MAX_SPEED * Math.min((EDGE - y) / EDGE, 1);
|
||||
} else if (y > h - EDGE) {
|
||||
speedRef.current = MAX_SPEED * Math.min((y - (h - EDGE)) / EDGE, 1);
|
||||
} else {
|
||||
speedRef.current = 0;
|
||||
}
|
||||
} else {
|
||||
speedRef.current = 0;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
// rAF loop for smooth scrolling
|
||||
const loop = () => {
|
||||
const node = innerRef.current;
|
||||
|
||||
if (node && speedRef.current !== 0) {
|
||||
node.scrollTop += speedRef.current;
|
||||
}
|
||||
|
||||
rafRef.current = requestAnimationFrame(loop);
|
||||
};
|
||||
|
||||
rafRef.current = requestAnimationFrame(loop);
|
||||
|
||||
return () => {
|
||||
if (rafRef.current) {
|
||||
cancelAnimationFrame(rafRef.current);
|
||||
}
|
||||
};
|
||||
}, [isDragging]);
|
||||
|
||||
const ref = useMergeRefs(innerRef, mouse.reference, externalRef);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={className}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { importLazyComponent } from '@cloudbeaver/core-blocks';
|
||||
|
||||
export const DNDScrollContainer = importLazyComponent(() => import('./DNDScrollContainer.js').then(m => m.DNDScrollContainer));
|
||||
@@ -27,6 +27,7 @@ export * from './DragAndDrop/DNDPreviewLoader.js';
|
||||
export * from './DragAndDrop/DNDProviderLoader.js';
|
||||
export * from './DragAndDrop/useDNDBox.js';
|
||||
export * from './DragAndDrop/useDNDData.js';
|
||||
export * from './DragAndDrop/DNDScrollContainerLoader.js';
|
||||
|
||||
export * from './Form/Components/IBaseFormProps.js';
|
||||
export * from './Form/Components/BaseFormLazy.js';
|
||||
|
||||
+3
-2
@@ -26,6 +26,7 @@ import {
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events';
|
||||
import { EObjectFeature, type NavNode, NavNodeInfoResource, NavTreeResource, ROOT_NODE_PATH } from '@cloudbeaver/core-navigation-tree';
|
||||
import { DNDScrollContainer } from '@cloudbeaver/core-ui';
|
||||
|
||||
import { useNavTreeDropBox } from '../useNavTreeDropBox.js';
|
||||
import style from './ElementsTree.module.css';
|
||||
@@ -172,7 +173,7 @@ export const ElementsTree = observer(
|
||||
return (
|
||||
<>
|
||||
<ElementsTreeTools tree={tree} settingsElements={settingsElements} />
|
||||
<div ref={treeMergedRef} className={s(styles, { treeBox: true })}>
|
||||
<DNDScrollContainer ref={treeMergedRef} className={s(styles, { treeBox: true })} isDragging={!!dndBox.state.context}>
|
||||
<ElementsTreeContext.Provider value={context}>
|
||||
<div className={s(styles, { box: true }, className)}>
|
||||
<FolderExplorer state={folderExplorer}>
|
||||
@@ -210,7 +211,7 @@ export const ElementsTree = observer(
|
||||
</FolderExplorer>
|
||||
</div>
|
||||
</ElementsTreeContext.Provider>
|
||||
</div>
|
||||
</DNDScrollContainer>
|
||||
</>
|
||||
);
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user