Skip to content

Commit

Permalink
feat: virtual scroll
Browse files Browse the repository at this point in the history
  • Loading branch information
dineug committed Jan 12, 2024
1 parent 970e17c commit 9157ae8
Show file tree
Hide file tree
Showing 6 changed files with 377 additions and 4 deletions.
2 changes: 1 addition & 1 deletion packages/erd-editor-vscode/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vuerd-vscode",
"version": "1.0.8",
"version": "1.0.9",
"private": true,
"description": "Entity-Relationship Diagram Editor VSCode Extension",
"icon": "./assets/erd-editor.png",
Expand Down
2 changes: 1 addition & 1 deletion packages/erd-editor/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dineug/erd-editor",
"version": "3.1.2",
"version": "3.1.3",
"description": "Entity-Relationship Diagram Editor",
"type": "module",
"main": "./dist/erd-editor.js",
Expand Down
17 changes: 15 additions & 2 deletions packages/erd-editor/src/components/erd/Erd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import ErdContextMenu, {
import HideSign from '@/components/erd/hide-sign/HideSign';
import Minimap from '@/components/erd/minimap/Minimap';
import TableProperties from '@/components/erd/table-properties/TableProperties';
import VirtualScroll from '@/components/erd/virtual-scroll/VirtualScroll';
import ColorPicker from '@/components/primitives/color-picker/ColorPicker';
import { useContextMenuRootProvider } from '@/components/primitives/context-menu/context-menu-root/contextMenuRootContext';
import { Open } from '@/constants/open';
Expand Down Expand Up @@ -109,8 +110,18 @@ const Erd: FC<ErdProps> = (props, ctx) => {
};

const handleWheel = (event: WheelEvent) => {
event.preventDefault();
const $mod = isMod(event);
const { store } = app.value;
store.dispatch(streamZoomLevelAction$(event.deltaY < 0 ? 0.1 : -0.1));

store.dispatch(
$mod
? streamZoomLevelAction$(event.deltaY < 0 ? 0.1 : -0.1)
: streamScrollToAction({
movementX: event.deltaX * -1,
movementY: event.deltaY * -1,
})
);
};

const handleMove = ({ event, movementX, movementY }: DragMove) => {
Expand Down Expand Up @@ -141,7 +152,8 @@ const Erd: FC<ErdProps> = (props, ctx) => {
canUnselectAll &&
canHideColorPicker &&
!el.closest('.minimap') &&
!el.closest('.minimap-viewport');
!el.closest('.minimap-viewport') &&
!el.closest('.virtual-scroll');

if (canUnselectAll) {
const { store } = app.value;
Expand Down Expand Up @@ -336,6 +348,7 @@ const Erd: FC<ErdProps> = (props, ctx) => {
@wheel=${handleWheel}
>
<${Canvas} root=${root} canvas=${canvas} grabMove=${state.grabMove} />
<${VirtualScroll} />
<${Minimap} />
<${HideSign} root=${root} />
${state.dragSelect
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { css } from '@dineug/r-html';

export const vertical = css`
position: absolute;
top: 0;
right: 0;
width: 8px;
height: calc(100% - 8px);
overflow: hidden;
padding-top: 4px;
`;

export const horizontal = css`
position: absolute;
left: 0;
bottom: 0;
width: calc(100% - 8px);
height: 8px;
overflow: hidden;
padding-left: 4px;
`;

export const ghostThumb = css`
will-change: transform;
cursor: pointer;
&:hover > div {
background-color: var(--scrollbar-thumb-hover);
}
&[data-selected] > div {
background-color: var(--scrollbar-thumb-hover);
}
`;

const thumb = css`
background-color: var(--scrollbar-thumb);
border-radius: 4px;
`;

export const verticalThumb = css`
width: 4px;
height: 100%;
${thumb};
`;

export const horizontalThumb = css`
width: 100%;
height: 4px;
${thumb};
`;
150 changes: 150 additions & 0 deletions packages/erd-editor/src/components/erd/virtual-scroll/VirtualScroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { createRef, FC, html, ref } from '@dineug/r-html';

import { useAppContext } from '@/components/appContext';
import { scrollToAction } from '@/engine/modules/settings/atom.actions';

import { useVirtualScroll } from './useVirtualScroll';
import * as styles from './VirtualScroll.styles';

export type VirtualScrollProps = {};

const VirtualScroll: FC<VirtualScrollProps> = (props, ctx) => {
const app = useAppContext(ctx);
const {
state,
getWidthRatio,
getHeightRatio,
onScrollLeftStart,
onScrollTopStart,
} = useVirtualScroll(ctx);
const horizontal = createRef<HTMLDivElement>();
const vertical = createRef<HTMLDivElement>();

const handleMoveLeft = (event: MouseEvent) => {
const el = event.target as HTMLElement | null;
if (!el) return;

const canMove = !el.closest('.virtual-scroll-ghost-thumb');
if (!canMove) return;

const { store } = app.value;
const {
editor: { viewport },
settings,
} = store.state;
const ratio = getWidthRatio();
const $horizontal = horizontal.value;
const rect = $horizontal.getBoundingClientRect();
const clientX = event.clientX;

const x = clientX - rect.x;
const absoluteX = x / ratio;
const scrollLeft = absoluteX - viewport.width / 2;

store.dispatch(
scrollToAction({
scrollLeft: -1 * scrollLeft,
scrollTop: settings.scrollTop,
})
);

onScrollLeftStart(event);
};

const handleMoveTop = (event: MouseEvent) => {
const el = event.target as HTMLElement | null;
if (!el) return;

const canMove = !el.closest('.virtual-scroll-ghost-thumb');
if (!canMove) return;

const { store } = app.value;
const {
editor: { viewport },
} = store.state;
const ratio = getHeightRatio();
const $vertical = vertical.value;
const rect = $vertical.getBoundingClientRect();
const clientY = event.clientY;

const y = clientY - rect.y;
const absoluteY = y / ratio;
const scrollTop = absoluteY - viewport.height / 2;

store.dispatch(
scrollToAction({
scrollLeft: store.state.settings.scrollLeft,
scrollTop: -1 * scrollTop,
})
);

onScrollTopStart(event);
};

return () => {
const { store } = app.value;
const {
editor: { viewport },
settings: { width, height, scrollLeft, scrollTop },
} = store.state;

const wRatio = getWidthRatio();
const hRatio = getHeightRatio();
const w = viewport.width * wRatio;
const h = viewport.height * hRatio;
const left = -1 * scrollLeft * wRatio;
const top = -1 * scrollTop * hRatio;

const showHorizontal = viewport.width < width;
const showVertical = viewport.height < height;

return html`
${showHorizontal
? html`
<div
class=${['virtual-scroll', styles.horizontal]}
${ref(horizontal)}
@mousedown=${handleMoveLeft}
>
<div
class=${['virtual-scroll-ghost-thumb', styles.ghostThumb]}
style=${{
width: `${w}px`,
height: '100%',
transform: `translate(${left}px, 0px)`,
}}
?data-selected=${state.selected === 'horizontal'}
@mousedown=${onScrollLeftStart}
>
<div class=${styles.horizontalThumb}></div>
</div>
</div>
`
: null}
${showVertical
? html`
<div
class=${['virtual-scroll', styles.vertical]}
${ref(vertical)}
@mousedown=${handleMoveTop}
>
<div
class=${['virtual-scroll-ghost-thumb', styles.ghostThumb]}
style=${{
width: '100%',
height: `${h}px`,
transform: `translate(0px, ${top}px)`,
}}
?data-selected=${state.selected === 'vertical'}
@mousedown=${onScrollTopStart}
>
<div class=${styles.verticalThumb}></div>
</div>
</div>
`
: null}
`;
};
};

export default VirtualScroll;
Loading

0 comments on commit 9157ae8

Please sign in to comment.